How are ambiguous enum values resolved in C#?

2019-01-23 23:19发布

问题:

I checked the section of the C# language specification regarding enums, but was unable to explain the output for the following code:

enum en {
    a = 1, b = 1, c = 1,
    d = 2, e = 2, f = 2,
    g = 3, h = 3, i = 3,
    j = 4, k = 4, l = 4
}

en[] list = new en[] {
    en.a, en.b, en.c,
    en.d, en.e, en.f,
    en.g, en.h, en.i,
    en.j, en.k, en.l
};
foreach (en ele in list) {
    Console.WriteLine("{1}: {0}", (int)ele, ele);
}

It outputs:

c: 1
c: 1
c: 1
d: 2
d: 2
d: 2
g: 3
g: 3
g: 3
k: 4
k: 4
k: 4

Now, why would it select the third "1", the first "2" and "3", but the second "4"? Is this undefined behavior, or am I missing something obvious?

回答1:

This is specifically documented to be undocumented behaviour.

There is probably something in the way the code is written that will end up picking the same thing every time but the documentation of Enum.ToString states this:

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.

(my emphasis)

As mentioned in a comment, a different .NET runtime might return different values, but the whole problem with undocumented behaviour is that it is prone to change for no (seemingly) good reason. It could change depending on the weather, the time, the mood of the programmer, or even in a hotfix to the .NET runtime. You cannot rely on undocumented behavior.

Note that in your example, ToString is exactly what you want to look at since you're printing the value, which will in turn convert it to a string.

If you try to do a comparison, all the enum values with the same underlying numerical value is equivalent and you cannot tell which one you stored in a variable in the first place.

In other words, if you do this:

var x = en.a;

there is no way to afterwards deduce that you wrote en.a and not en.b or en.c as they all compare equal, they all have the same underlying value. Well, short of creating a program that reads its own source.