Many Questions: Switch On Enum


Just a quick one this week: 


 


Why is it that you cannot use enum constants in a switch statement's cases without first casting them to type int?


 


Often you will want to use Enum constants as case labels in switch statements. Sometimes, the compiler will complain and require a cast to int on each case label. This will look something like this:


 


    enum Color { Red, Green, Blue };


 


    int i = ...;


    switch (i)


    {


    // cast required! Aarg!


    case (int)Color.Red:    break;


    case (int)Color.Green:  break;


    case (int)Color.Blue:   break;


    }


 


The confusion stems from a subtle difference between C++ and C#. In C++, values of enum type are implicitly convertible to int. In C#, conversions between an enum type and its underlying type are explicit and require a cast. To maintain consistency, the requirement for the cast carries over into the use of enums in switch statements.


 


However, you can use expressions of enum type as case labels without a cast, but only in a type safe way. The rule is that the type of the expression being switched on, the ‘governing type’ of the switch statement in C# language spec terminology, must match the type of the expressions in the case labels.


 


For example:


 


    enum Color { Red, Green, Blue };


 


    Color c = ...;


    switch (c) // Governing type is ‘Color’ not ‘int’ so ...


    {


    // ... no cast required


    case Color.Red:    break;


    case Color.Green:  break;


    case Color.Blue:   break;


    }


 


 


One of the design goals for enums in C# was to treat them as first class types that were truly distinct from their underlying type. This is one of the subtle ways that this decision manifests itself in the language.


 


Peter


C# Guy


Comments (16)

  1. tzagotta says:

    Hi Peter, Can I ask an add-on question? Why are constant expressions required in the case statements? For example, consider this code:

    if (obj == myFirstObject)

    do_something();

    else if (obj == mySecondObject)

    do_something_else();

    else

    do_default_something();

    Why doesn’t the C# language allow for the data type of Object and non-const cases like myFirstObject, etc.?

    As a programmer, I see a switch statement conceptually the same as a series of chained ifs/else ifs. It would be nice to be able to do this.

  2. Ron says:

    I wish C# would implicitly cast enums into ints (or whatever its type specification is). I find myself having to resort to declarations such as:

    struct Bet {

    public const byte FOLD = 0;

    public const byte CHECK_CALL = 1;

    public const byte BET_RAISE = 2;

    }

    …to handle simple statements such as:

    return EV[Bet.CHECK_CALL] + EV[Bet.BET_RAISE];

    To do the above with an enum requires explicit casts:

    return EV[(int)Bet.CHECK_CALL] + EV[(int)Bet.BET_RAISE];

    This is both slower and uglier. I also wish I could do a cleaner foreach statement such as:

    foreach (byte bet in Bet)

    …instead of…

    foreach (byte bet in Enum.getValues(typeof(Bet)))

    In general, almost everytime I try to create an enum, I find myself reverting to the manually created set of byte constants to keep the code as clean looking as possible and to avoid the performance reductions of all the casts.

  3. Protagonist says:

    I wish C# would implicitly cast enums into ints. I find myself having to resort to declarations such as:

    struct Bet {

    public const byte FOLD = 0;

    public const byte CHECK_CALL = 1;

    public const byte BET_RAISE = 2;

    }

    …to handle simple statements such as:

    return EV[Bet.CHECK_CALL] + EV[Bet.BET_RAISE];

    To do the above with an enum requires explicit casts:

    return EV[(int)Bet.CHECK_CALL] + EV[(int)Bet.BET_RAISE];

    This is both slower and uglier. I also wish I could do a cleaner foreach statement such as:

    foreach (byte bet in Bet)

    …instead of…

    foreach (byte bet in Enum.getValues(typeof(Bet)))

    In general, almost every time I try to create an enum, I find myself reverting to the manually created set of byte constants to keep the code as clean looking as possible and to avoid the performance reductions of all the casts.

  4. Andrew Deren says:

    tzagotta,

    c# (and other language) require constants because the compiler generates jump table to jump to appropriate case brach.

    If you have if/elseif/elseif/else, each condition of if/elseif has to be evaluated,

    with switch the control just jumps to appropriate branch (but need constant for that)

  5. Protagonist says:

    Perhaps even cleaner and faster would be:

    enum Bet {FOLD, CHECK_CALL, BET_RAISE};

    float[] EV = new float[Bet]; // creates an enum array of Bets

    …with type-safe array indexing…

    return EV[Bet.CHECK_CALL]; // clean and fast

    Bet bet = …;

    return EV[bet]; // clean and fast

    return EV[0]; // caught by compiler? not sure if necessary or desired

    When enum arrays are allocated, only the minimum through the maximum values are allocated. So in the case of the ‘Bet’ enum, the allocation would be the same as if the statement read:

    float[] EV = new float[3];

    I would like something like the above. Is this possible, or are there gotchas that make this not work?

  6. tzagotta says:

    Protagonist, I think the concept you are looking for is not an "array" – you want to associate a set of enum values with a set of floats. I think a better data structure for this would be Dictionary in .NET or std::map in C++. Here is a sample in C#:

    private enum Bet { FOLD, CHECK_CALL, BET_RAISE };

    static void Main(string[] args)

    {

    Dictionary<Bet, float> EV = new Dictionary<Bet,float>();

    EV.Add(Bet.FOLD, 2.3f);

    EV.Add(Bet.CHECK_CALL, 5.5f);

    EV.Add(Bet.BET_RAISE, 11f);

    foreach (KeyValuePair<Bet, float> Item in EV)

    Console.WriteLine(String.Format("{0} -> {1}", Item.Key.ToString(), Item.Value));

    }

    What is wrong with array? Array indexing is only efficient with a contiguous range of indices. Since enum values can be set to any numeric value, this is a problem, for example, enum {apple, pear = 555, orange = 2883}.

    Second, the intializer depends on the order (values) of the enums. If you change the order, the initialiers are broken. Of course, if you add one more, you always have to add another initializer.

    In general, I like the idea of keeping enum and int separate. Constants should be declared using const int, and enumerations with enum. These are two different constructs with different semantics. I never liked how C/C++ blurred the distinction between them.

  7. Protagonist says:

    Tzagotta,

    While a Dictionary certainly works, it doesn’t compare to the speed of array indexing. As you state: "Array indexing is only efficient with contiguous range of indices". When a fairly contiguous set of enum values are defined, being able to index that densely populated enum array using those values is a cleaner and faster data structure.

    When an enum contains widely disperse values (as with your apple, pear, and orange example), using a Dictionary is a more efficient use of storage.

    I don’t why you added:

    "Second, the intializer depends on the order (values) of the enums. If you change the order, the initialiers are broken. Of course, if you add one more, you always have to add another initializer."

    The suggested "enum array" doesn’t care how the enum is initialized. When an enum array is created, as in:

    float[] EV = new float[Bet]; // creates an enum array of Bets

    …all that matters is the range of ‘Bet’ (the lowest and highest constant values, in this case 0 and 2). The order of the enum members and "if you add one more", while it changes the values of the members, is not a problem in determining the range of the values.

  8. So thanks to Peter Hallam to clear up one thing that always bother me but couldn’t be bothered to look it up….

  9. MSDN Archive says:

    Good question … I posted a full reply as this week’s "Many C# Questions" blog entry. http://blogs.msdn.com/peterhal/archive/2005/08/12/451124.aspx

    Peter

  10. MSDN Archive says:

    The design of enums and ints is a long and complicated story … great idea for another blog on another week. Here are some quick commments though:

    – casting an enum to an int is just as efficient as using a regular int, though it certainly doesn’t look as nice.

    – using Enum.GetValues() is many thousands of times slower than just an array index however.

    – The choice of enums vs. const ints is a tricky one. I find that if I’m using an enum often as an index into some arrays it can be best to package all the arrays into a single array of Info objects with a set of strongly typed accessors. But again, that’s a whole other story…

    Peter

  11. Last updated 12 Jan 2008 MSDN Info on C# XML Standard Comments (like Java’s Javadoc> for methods, variables, etc. Flushing Output Buffers in C# Comparisons of C# vs. Java or C++ DialogResult ‘Enumeration’ Explanations How do I use Enum with…

  12. "Where would you use a Property as opposed to a method?" "Where would you use static as opposed to instance methods and fields? "I’ve noted that you don’t need to use the Convert.ToString function on a List Box control…

  13. Updated 02 Feb 2008: After trying repeatedly to use the "Discussion" part of the Web site for the C# Programming courses I’ve taken, and getting slapped down by the instructor for it whenever I offered advice about helpful Web…

  14. Last updated 07 Feb 2008 Here’s a list of some of my own common questions and the answers I received from taking classes or mostly from researching MSDN and the Web. "When/where would you use a Property as opposed…

Skip to main content