I hope everyone had a good fourth of July weekend. I certainly did. I spent the weekend hiking around the Olympic peninsula with my girlfriend and capped it off watching the fireworks here in Seattle. On to our question of the week:

We get a lot of requests for addditions to the C# language and today I'm going to talk about one of the more common ones - switch on type. Switch on type looks like a pretty useful and straightforward feature: Add a switch-like construct which switches on the type of the expression, rather than the value. This might look something like this:

switch typeof(e) {
case int:    ... break;
case string: ... break;
case double: ... break;
default:     ... break;
}

This kind of statement would be extremely useful for adding virtual method like dispatch over a disjoint type hierarchy, or over a type hierarchy containing types that you don't own. Seeing an example like this, you could easily conclude that the feature would be straightforward and useful. It might even get you thinking "Why don't those #*&%$ lazy C# language designers just make my life easier and add this simple,  timesaving language feature?"

Unfortunately, like many 'simple' language features, type switch is not as simple as it first appears. The troubles start when you look at a more significant, and no less important, example like this:

class C {}
interface I {}
class D : C, I {}

switch typeof(e) {
case C: ... break;
case I: ... break;
default: ... break;
}

Here the set of cases overlap. When ‘e’ is of type ‘D' both 'case I' and 'case C' are applicable. Now what? As language designers, we need to precisely specify what happens in cases like these. The choices to deal with this kind of situation are:

  • process the case tests in textual order
  • make definition of potentially overlapping cases a compile time error

Processing the cases in textual order would make the above switch semantically different from this:

switch typeof(e) {
case I: ... break;
case C: ... break;
default
: ... break;
}

This runs completely counter to people’s intuition about switch. Programmers would be extremely surprised to learn that reordering the case labels had an affect on which case was chosen.

Better yet, what about this:

switch typeof(e) {
default: ... break;
case
I: ... break;
case C: ... break;
}

Would the default case always be chosen? Ouch!

Alternatively, making definition of potentially overlapping cases a compile error would not yield a useful feature. It would make switching on interfaces an error for virtually every useful case, and would also make switching on an extensible type hierarchy - like System.Windows.Forms.Control -  also difficult if not impossible.

So the conclusion is that we thought pretty deeply about this feature, but couldn't find a way to add switch on type in a way which is both intuitive and broadly useful. This situation is not uncommon in language design. The C# language design team has discarded many potential features for similar reasons.

This example ilustrates what is the most important lesson I've learned from Anders Hejlsberg while I've been on the C# language design team. Often the result of good design is to cut the feature.

Peter
C# Guy