Inconsistent Enum Casting Results in C#

I was recently revisiting some of the strange behavior of C# related to casting an integer into an existing enum where some enum integer values are assigned explicitly and some are assigned in order. Using LinqPad 4, I built the following enum:

public enum Hurf {
    Foo,
    Bar,
    Derp,
    Duh = 2,
    Lerp,
    Mur,
    Dur = 3
}

Using the following Main block body in C# Program mode, I perform the following casts:

Console.WriteLine((Hurf)2);
Console.WriteLine((int)Hurf.Derp);

Console.WriteLine((Hurf)3);
Console.WriteLine((int)Hurf.Lerp);

This results in the following output:

Derp
2
Dur
3

Strange, it appeared that in the first cast, since there were two items with the integer value of 2, that it defaulted to the first one, which was "Derp". With the same logic, I perform another cast using integer value 3, and this time it results in the SECOND value of 3, which is "Dur".

I decide to add another item to the bottom of the enum directly below "Dur" as "Test" and set it to 0.

public enum Hurf {
    Foo,
    Bar,
    Derp,
    Duh = 2,
    Lerp,
    Mur,
    Dur = 3,
    Test = 0
}

This doesn't change the behavior of the first cast, but now the result for the second cast is "Lerp", which would be consistent with it casting to the first value of 3 that was set in the enumeration.

Duh
2
Lerp
3

For obvious reasons, I wouldn't rely on this type of casting to begin with, but it would be nice to know why this behavior exists and is affected by an arbitrary manual assignment at the bottom of the enum list. (Also, setting "Test" to a value above 3 causes the original behavior to return.)

Jon Skeet
people
quotationmark

It's not the casting that's the problem - it's the conversion to a string representation. Lerp and Dur are the same value - you can't distinguish between them.

Indeed, if you call ToString() directly on them, like this:

Console.WriteLine(Hurf.Lerp.ToString());
Console.WriteLine(Hurf.Dur.ToString());

... you should expect to see the same output twice. It just isn't well-defined which output you'll get.

The documentation for Enum.ToString() explicitly states:

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.

people

See more on this question at Stackoverflow