More peculiarites of enum


The well known (or moderately known fact): C# enums can contain any value supported by its base type and not just the ones specified in the enum. Lets consider the following enum.

enum WeekDay
{
Monday = 1,
Tuesday = 2,
Wednesday = 3,
Thursday = 4,
Friday = 5,
Saturday = 6,
Sunday = 7
}


Even though the enum supports values from 1 to 7 you can do the following.

WeekDay day = (WeekDay)40;
Console.WriteLine(day);

This prints out 40 without any error.


Now comes the lesser known fact: C# 1.2 spec sez “An Implicit enumeration conversion permits the decimal-integer-literal 0 to be converted to enum-type”. This means the following is valid code

WeekDay day = 0;
Console.WriteLine(day);

Note that even though 0 is not supported it is implicitely casted to the enum!!! No idea why this is supported.

Comments (9)

  1. zproxy says:

    An enum value of 0 should be always reserved for None, Empty, Default or alike, thus assigning 0 allows to initialize the enum. It should have the same effect as default(MyEnumType).

    "Do name the zero value of flags enumerations None. For a flags enumeration, the value must always mean all flags are cleared.

    Note"

    http://msdn2.microsoft.com/en-us/library/ms229062.aspx

  2. You have pointed to Flags enumeration where it makes perfect sense to have a zero.

    But in our case this is not a bit flag and there is no default or empty weekday, nor can you have monday and tuesday at the same time (with an OR).

  3. Sam Judson says:

    Enumerations are a CLR value type, stored in the method stack as an Int32, which defaults to 0 anyway – the following two code segments have exactly the same end result:

    WeekDay day;

    WeekDay day = 0;

    The C# compiler will however consider the first variable ‘unitialised’.

  4. Peter Ritchie says:

    I think it’s less to do with the guideline of always defining a enum member with the value 0 than to do with the fact that an enum will always initialize to 0 before the first assignment.  There’s really no point in throwing an error for assigning a value (0) to a enum type when that same value was used to initialize it, without error.

    An in fact, in your example "Weekday day = 0;", the assignment of zero (0) is redundant; day will have been initialized with 0 already.  It could be that this is "supported" because it is optimized out (I’m not sure).

  5. Mark Steward says:

    I’m pretty sure it would have to be because (as you mentioned) Enums are used for flags as well as for lists.

    But of course, a lot of enums come from protocol definitions, so having to mentally add a zero case to every enum would be onerous, for very little extra protection (since enums are initialised to 0, it’s not risky like having a zero pointer).

  6. Shahar Prish says:

    Peter Ritchie:

    C# requires that you initialize any and all local variables.

    You are confusing this with member variables that do not have to be initialized.

    (This is a C# thing, not a CLR/MSIL thing – the CLR, as somebody mentioned, will automatically initialize it if the code does not do that..)

  7. Michael Buen says:

    can you request to Visual Studio 2008 team, to straight out C# 3.0 treatment of enum?  because it is very confusing.

    it is not clean to have 0 moonlighting as enum.

    an example:

    enum PgType { Varchar, Date, Integer };

    static void Main()

    {

    AddField("Name", "Michael Buen");

    AddField("JobToSeek", PgType.Varchar);

    AddField("Age", 20);

    AddField("WorkExperience", 0);

    }

    static void AddField(string name, PgType t)

    {

    Console.WriteLine(name + ": just add field");

    }

    static void AddField(string name, object value)

    {

    Console.WriteLine(name + ": add field and assign value at the same time, also get the type");

    }

    output is:

    Name: add field and assign value at the same time, also get the type

    JobToSeek: just add field

    Age: add field and assign value at the same time, also get the type

    WorkExperience: just add field

    Note: the WorkExperience should be: add field and assign a value at the same time.

    to illustrate again the problem:

    http://plus.kaist.ac.kr/~shoh/postgresql/Npgsql/apidocs/Npgsql.NpgsqlParameterCollection.Add_overload_3.html

    http://cleveralias.blogs.com/thought_spearmints/2004/01/more_c_enum_wac.html

    eric gunnerson’s reasoning:

       if ((myVar & MyEnumName.ColorRed) != (MyEnumName) 0)

       which we thought was difficult to read. One alernative was to define a zero entry:

       if ((myVar & MyEnumName.ColorRed) != MyEnumName.NoBitsSet)

       which was also ugly.

       We therefore decided to relax our rules a bit, and permit an implicit conversion from the literal zero to any enum type, which allows you to write:

       if ((myVar & MyEnumName.ColorRed) != 0)

       which is why PlayingCard(0, 0) works.

    that relaxing rule was favored because of bitwise elegance, but the C# team didn’t see the drastic effect on programmer’s strong faith on C#’s strong-typing.  0 should resolve to object if there is no integer overload function.  0 should never become an enum type in anyways, just like the value 20 should resolve to object, not enum.  i hope they will fix this in C# 3.0.  C# enum zero looks like a gotcha, because it always needs an explanation

  8. Michael Buen says:

    they should have make @0 to indicate enum zero.   0 shouldn’t be implicitly cast to in any ways, just like any number.

    VS team should have make this:

      if ((myVar & MyEnumName.ColorRed) != 0)

    to:

      if ((myVar & MyEnumName.ColorRed) != @0)

    so there will be no more implicitly casted zero to enum gotcha 🙂

  9. Michael Buen says:

    they should have make @0 to indicate enum zero.   0 shouldn’t be implicitly cast to in any ways, just like any number.

    VS team should have make this:

      if ((myVar & MyEnumName.ColorRed) != 0)

    to:

      if ((myVar & MyEnumName.ColorRed) != @0)

    so there will be no more implicitly casted zero to enum gotcha 🙂