When writing a switch statement, there appears to be two limitations on what you can switch on in case statements.
For example (and yes, I know, if you're doing this sort of thing it probably means your object-oriented (OO) architecture is iffy - this is just a contrived example!),
Type t = typeof(int);
switch (t) {
case typeof(int):
Console.WriteLine("int!");
break;
case typeof(string):
Console.WriteLine("string!");
break;
default:
Console.WriteLine("unknown!");
break;
}
Here the switch() statement fails with 'A value of an integral type expected' and the case statements fail with 'A constant value is expected'.
Why are these restrictions in place, and what is the underlying justification? I don't see any reason why the switch statement has to succumb to static analysis only, and why the value being switched on has to be integral (that is, primitive). What is the justification?
Judah's answer above gave me an idea. You can "fake" the OP's switch behavior above using a
Dictionary<Type, Func<T>
:This allows you to associate behavior with a type in the same style as the switch statement. I believe it has the added benefit of being keyed instead of a switch-style jump table when compiled to IL.
I agree with this comment that using a table driven approach is often better.
In C# 1.0 this was not possible because it didn't have generics and anonymous delegates. New versions of C# have the scaffolding to make this work. Having a notation for object literals is also helps.
True, it doesn't have to, and many languages do in fact use dynamic switch statements. This means however that reordering the "case" clauses can change the behaviour of the code.
There's some interesting info behind the design decisions that went into "switch" in here: Why is the C# switch statement designed to not allow fall-through, but still require a break?
Allowing dynamic case expressions can lead to monstrosities such as this PHP code:
which frankly should just use the
if-else
statement.According to the switch statement documentation if there is an unambiguous way to implicitly convert the the object to an integral type, then it will be allowed. I think you are expecting a behavior where for each case statement it would be replaced with
if (t == typeof(int))
, but that would open a whole can of worms when you get to overload that operator. The behavior would change when implementation details for the switch statement changed if you wrote your == override incorrectly. By reducing the comparisons to integral types and string and those things that can be reduced to integral types (and are intended to) they avoid potential issues.