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

  • Anonymous
    August 01, 2005
    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.

  • Anonymous
    August 01, 2005
    The comment has been removed

  • Anonymous
    August 01, 2005
    The comment has been removed

  • Anonymous
    August 02, 2005
    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)

  • Anonymous
    August 02, 2005
    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?

  • Anonymous
    August 02, 2005
    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.

  • Anonymous
    August 02, 2005
    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.

  • Anonymous
    August 04, 2005
    So thanks to Peter Hallam to clear up one thing that always bother me but couldn't be bothered to look it up....

  • Anonymous
    August 12, 2005
    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

  • Anonymous
    August 12, 2005
    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

  • Anonymous
    January 13, 2008
    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...

  • Anonymous
    January 30, 2008
    The comment has been removed

  • Anonymous
    February 02, 2008
    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...

  • Anonymous
    February 07, 2008
    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...

  • Anonymous
    August 21, 2008
    PingBack from http://dotnetwitter.wordpress.com/2008/08/21/links-for-2008-08-21/

  • Anonymous
    June 16, 2009
    PingBack from http://lowcostcarinsurances.info/story.php?id=4686