Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why (and how) does the order of an Enum influence the ToString value?

I have problems with the "order" of the values of an enum. It's a little difficult to explain, that's why I wrote up some code:

class Program
{
    public enum EnumA
    {
        One = 1,
        Two = One,
        Three = Two,
        Four = 4
    }

    public enum EnumB
    {
        One = 1,
        Two = One,
        Four = 4,
        Three = Two
    }

    public enum EnumC
    {
        Two = One,
        Three = Two,
        Four = 4,
        One = 1
    }

    static void Main(string[] args)
    {
        Console.WriteLine("Enum A:");
        Console.WriteLine(EnumA.One);
        Console.WriteLine(EnumA.Two);
        Console.WriteLine(EnumA.Three);
        Console.WriteLine(EnumA.Four);
        Console.WriteLine();

        Console.WriteLine("Enum B:");
        Console.WriteLine(EnumB.One);
        Console.WriteLine(EnumB.Two);
        Console.WriteLine(EnumB.Three);
        Console.WriteLine(EnumB.Four);
        Console.WriteLine();

        Console.WriteLine("Enum C:");
        Console.WriteLine(EnumC.One);
        Console.WriteLine(EnumC.Two);
        Console.WriteLine(EnumC.Three);
        Console.WriteLine(EnumC.Four);
        Console.WriteLine();

        Console.ReadLine();
    }
}

The output is:

Enum A: Two Two Two Four

Enum B: Three Three Three Four

Enum C: One One One Four

My question is: WHY!? I can't find the logic to the output. Most of the time there is some logic to be found, so I hope you guys can shine some light on this issue.

I used VS2010 / .Net 4.0 to compile and run the code.

like image 344
Kees C. Bakker Avatar asked Jan 04 '11 10:01

Kees C. Bakker


2 Answers

The behaviour is specified to be "undefined" (I thought I'd spotted a pattern just now, but apparently not.) The documentation explicitly calls this out:

If multiple enumeration members have the same underlying value and you attempt to retrieve the string representation of an enumeration member's name based on its underlying value, your code should not make any assumptions about which name the method will return.

Either make your enum values distinct, or explicitly create a map from value to desired name.

like image 176
Jon Skeet Avatar answered Oct 24 '22 12:10

Jon Skeet


The first thing to observe, if you decompile the IL, is that the calls to WriteLine all look remarkably similar:

    L_000c: ldc.i4.1 
    L_000d: box ConsoleApplication2.Program/EnumA
    L_0012: call void [mscorlib]System.Console::WriteLine(object)
    L_0017: nop 
    L_0018: ldc.i4.1 
    L_0019: box ConsoleApplication2.Program/EnumA
    L_001e: call void [mscorlib]System.Console::WriteLine(object)
    L_0023: nop 
    L_0024: ldc.i4.1 
    L_0025: box ConsoleApplication2.Program/EnumA
    L_002a: call void [mscorlib]System.Console::WriteLine(object)
    L_002f: nop 
    L_0030: ldc.i4.4 
    L_0031: box ConsoleApplication2.Program/EnumA
    L_0036: call void [mscorlib]System.Console::WriteLine(object)
    L_003b: nop 
    L_003c: call void [mscorlib]System.Console::WriteLine()
    L_0041: nop 

That is, the loading of these enum values is loading the value "1" three times, and then calling WriteLine. So we should not be surprised that the 1st 3 calls all result in the same value.

I've tried a few experiments, but can't point to any particular (undocumented) behaviour you can rely on to predict which value will be printed.

like image 38
Damien_The_Unbeliever Avatar answered Oct 24 '22 12:10

Damien_The_Unbeliever