Dot Net Perls
C#

Switch Statement

by Sam Allen

Problem

Evaluate the switch statement and block. It tests a series of strings or other value types against an input parameter. For example, we want to see if a string is one of 5 known values, and return true if that is the case. Here we are focusing on string types, as we have a series of string values we need to examine.

Solution: C#

Our possibilities here are the if/else statement chains and the switch statement. I will delve into the IL (Intermediate Language) and see what the C# compiler is doing to our code. As we will see, a switch on strings can be optimized specially by the compiler. Here are some examples of switch.

/// <summary>
/// An example class containing several switch statements.
/// </summary>
static class SwitchTest
{
    /// <summary>
    /// Perform and example switch statement, using 18 case strings.
    /// </summary>
    /// <param name="testValue">An example input value we need to switch.</param>
    /// <returns>The resulting value from the switch.</returns>
    static public int SwitchBig(string testValue)
    {
        switch (testValue)
        {
            case "dia":
            case "dia.":
                {
                    return 0;
                }
            case "ht":
            case "height":
                {
                    return 1;
                }
            case "radius":
            case "reach":
            case "post dist":
            case "prior dist":
            case "size part":
            case "planting size":
            case "spread":
            case "diameter":
            case "diameter estimate":
            case "range":
            case "size":
            case "center dist":
            case "crown rad":
            case "number of trunks": // 18 case statements!
                {
                    return 2;
                }
        }
        return -1; // same as 'default'
    }

    /// <summary>
    /// Example switch statement with four case statements.
    /// </summary>
    /// <param name="testValue">Example string value.</param>
    /// <returns>Value from the switch.</returns>
    static public int SwitchMedium(string testValue)
    {
        switch (testValue)
        {
            case "dia":
            case "dia.":
                {
                    return 0;
                }
            case "ht":
            case "height":
                {
                    return 1;
                }
            default: // nothing above matches!
                {
                    return -1;
                }
        }
    }

    /// <summary>
    /// Example method for a small switch block of 2 string cases.
    /// </summary>
    /// <param name="testValue">Switch on this value.</param>
    /// <returns>Value from the switch.</returns>
    static public int SwitchSmall(string testValue)
    {
        switch (testValue)
        {
            case "ht":
            case "height":
                {
                    return 1;
                }
            default: // nothing above matches!
                {
                    return -1;
                }

        }
    }

    /// <summary>
    /// Example if/else chain of 2 cases.
    /// </summary>
    /// <param name="testValue">Example value.</param>
    /// <returns>The result from the comparison.</returns>
    static public int IfSmall(string testValue)
    {
        if (testValue == "ht")
        {
            return 1;
        }
        else if (testValue == "height")
        {
            return 1;
        }
        return -1; // nothing matched.
    }
}

Switch String Optimization

In the first example above, there are around 18 case statements. You could check each item, one by one, but the better way to do it is to use a Dictionary and then have constant lookup time. The Dictionary can provide a nearly instant lookup for 1,000's of keys! The next screenshot is of the IL from the first switch example. It is automatically compiled into a Dictionary.

The above IL code shows that the strings "ht" and "height" are actually added to the Dictionary using the Add method you use in your own code. It assigns the value of those keys to 1, which is the value returned from the cases in the code.

IL_0033:  dup
IL_0034:  ldstr      "ht"
IL_0039:  ldc.i4.2
IL_003a:  call       instance void class
[mscorlib]System.Collections.Generic.Dictionary`2
<string,int32>::Add(!0, !1)

IL_003f:  dup
IL_0040:  ldstr      "height"
IL_0045:  ldc.i4.3
IL_0046:  call       instance void class
[mscorlib]System.Collections.Generic.Dictionary`2
<string,int32>::Add(!0, !1)

Comments

The last two example methods I showed above do not produce the exact same IL code, but I suspect this would be inconsequential with the JIT compiler. I suggest that it is best to use if/else when the number of items being tested is very short (2 or 3), as that also seems to make clearer code. Here are some important points.

Conclusion

A large switch on a string value will be faster than the equivalent if/else chain, as it will be turned into a Dictionary for constant lookup time. For small methods, the if/else may be smaller and simpler, and for non-string types such as int, there may be little difference. Switch gives the compiler hints about ways to optimize your code. Use switch for testing a value against more than ~5 strings.

© 2008 Sam Allen. All rights reserved.

Ads by The Lounge