Many C# Questions: Switching on non-constant values.

I finally decided to play with the style settings on my blog. As you may have guessed, I'm a bit of a newbie when it comes to websites and blogging. Let me know what you think of the new look. Last weeks posting generated some great comments. Tzagotta asks:

Why are constant expressions required in case labels? I see a switch statement conceptually the same as a series of chained ifs/else ifs.

This is an interesting question. It seems reasonable that you should be able to switch on an expression of any type, and have case labels which can take any value – even values which are not compile time constants. A simple example would look something like this:

class Program {

    static void Main() {

        object myFirstObject = ...;

        object mySecondObject = ...;

        object obj = ...;

        switch (obj) {

        case myFirstObject: ...; break;

        case mySecondObject: ...; break;

        default: ...; break;

        }

    }

}

which would be equivalent to this code:

class Program {

    static void Main() {

        object myFirstObject = ...;

        object mySecondObject = ...;

        object obj = ...;

        if (obj == myFirstObject) {

           ...

        } else if (obj == mySecondObject) {

           ...

        } else {

           ...

        }

    }

}

At first glance this seems like a great idea – the switch is slightly more compact and readable than the chained ifs, both desirable characteristics of a programing language. If the construct could easily be limited to this simple example it might be a reasonable language extension to consider. Unfortunately, things are not as simple as they appear at first glance…

Firstly, what happens when myFirstObject is equal to mySecondObject? Well that depends on which case label came first in your switch. The order of the case labels becomes significant in determining which block of code gets executed. Because the case label expressions are not constant the compiler cannot verify that the values of the case labels are distinct, so this is a possibility which must be catered to. This runs counter to most programmers’ intuition about the switch statement in a couple of ways. Most programmers would be surprised to learn that changing the order of their case blocks changed the meaning of their program. To turn it around, it would be surprising if the expression being switched on was equal to an expression in a case label, but control didn’t go to that label.

Secondly, allowing non-constant expressions as case labels lets in some significantly less desirable code as well. For example, this code would also be legal:

        switch (obj) {

        case myFirstObject: ...; break;

        case mySecondObject: ...; break;

        case EraseMyHardDrive(): ...; break;

        default: ...; break;

        }

In C#, any non-constant expression can yield side effects. There is currently no notion of a non-constant expression which does not have side effects in C#. Seeing code like this will leave the reader scratching their head wondering when their hard drive will get erased.

And lastly, programmers coming from a C/C++ background will expect that the execution of a switch statement will be fast and that it will take about the same time to reach any particular case label. When the case labels are constant integral values (or strings), the compiler can do some great optimizations to make the execution speed of the construct match the coder’s expectation. With non-constant case labels, getting to the 100th case label would take significantly more time than getting to the 10th case label. Again, this would be surprising to the programmer.

Peter

C# Guy