Monday, February 21, 2011

Making a switch-like construct with types in C#

Update: this is more of an exploration post, not something serious on how to achieve the desired result. For a construct that actually works like a switch, check this post out: Creating a C# construct that acts like a more complex switch/case

Many a time I had to select a different behaviour based on the type of a boxed object. The usual solution for this is an ugly stream of if statements that check for the type and then return if found. A while ago I was writing about the dynamic keyword and how it can be used to declare a method for each specific type that is then executed from a simple method having the boxed object as a parameter. Some people liked it, some did not. What would have been nice is a switch statement that works with types, something like:
switch(obj.GetType()) 
{
case typeof(MyClass1):
// do something with obj
break;
case typeof(MyClass2):
// do something else with obj
break;
default:
throw new NotSupportedException();
}
Alas, this is impossible in C# due to the fact that typeof(class) is not considered a constant, but a method call.

What I am about to present to you is in no way a best practice, but something that I've cropped up in my wild musings on code: why not use the try/catch blocks as a switch statement? The idea is simple: create a generic Exception class then throw it based on the type of the object, then catch the specific generic Exception class. Here is the code, enjoy:
class Program
{
public class BaseClass
{
}

public class InheritedClass1 : BaseClass
{
}

public class InheritedClass2 : BaseClass
{
}

public class TypeCatchBlockException : Exception
{
public static void Throw(object obj) 
{
doThrow((dynamic)obj);
}

private static void doThrow<T>(T obj)
{
throw new TypeCatchBlockException<T>();
}

/*public static void Throw(object obj)
      {
        var type=obj==null?null:obj.GetType();
        var exceptionTypeName=typeof(TypeCatchBlockException)+"`1";
        Type generic=Type.GetType(exceptionTypeName).MakeGenericType(new[]{type});
        throw (Exception)Activator.CreateInstance(generic);
      }*/
}

public class TypeCatchBlockException<T> : TypeCatchBlockException
{
}

static void Main(string[] args)
{
showObjectType(new InheritedClass2());
showObjectType(new BaseClass());
showObjectType(new InheritedClass1());
showObjectType(new object());
Console.ReadLine();
}

private static void showObjectType(object obj)
{
try
{
TypeCatchBlockException.Throw(obj);
}
catch (TypeCatchBlockException<BaseClass>)
{
Console.WriteLine("BaseClass object");
}
catch (TypeCatchBlockException<InheritedClass1>)
{
Console.WriteLine("InheritedClass1 object");
}
catch (TypeCatchBlockException<InheritedClass2>)
{
Console.WriteLine("InheritedClass2 object");
}
catch (TypeCatchBlockException)
{
Console.WriteLine("Unsupported object");
}
}
}


So there is the simple class TypeCatchBlockException that has a Throw method. You have the dynamic implementation as well as the reflection implementation in the commented region. You execute Throw with an object inside a try block and then you add a catch block for each generic version of the exception for each supported type. The last block is reached if none of the supported types were encapsulated into the exception object.

Executing the console application in the code block results in:
InheritedClass2 object
BaseClass object
InheritedClass1 object
Unsupported object


Update: Some people didn't understand this was mostly a joke, so I needed to clarify it. We do need type switches and someone should implement them as a language construct. Throwing exceptions is expensive and the solution in this blog post would not be recommended for any production software.

But here is a nice solution for type switching using lambda expressions: Switching on Types.

0 comments: