32

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?

Resigned June 2023
  • 4,638
  • 3
  • 38
  • 49

1 Answers1

36

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.

Lasse V. Karlsen
  • 380,855
  • 102
  • 628
  • 825
  • At the very least, "every time" very likely won't apply when you run the program on multiple different implementations/versions of .NET. –  Jul 20 '14 at 16:42
  • That may be true, but however stable or unstable that piece of code is, it is still undocumented. – Lasse V. Karlsen Jul 20 '14 at 16:43
  • Yes. My issue is not with that, but with your answer: your answer suggests that programs can rely on getting the same enumerator name every time (even though I don't think that's what you meant to say), and the documentation doesn't agree with that suggestion. –  Jul 20 '14 at 16:46
  • 3
    The [reference source](http://referencesource.microsoft.com/#mscorlib/system/type.cs) does tell you why the output is the way it is. The 4.5.1 implementation is to get the enum names and values, sort them, then do a binary search for the desired value. Thus, it is dependent on sorting and searching implementations, which could easily change. The sort is actually an insertion sort because the common use case is a small number of values that are already sorted. The binary search is just `Array.BinarySearch`. – Mike Zboray Jul 20 '14 at 20:21
  • And a binary search is not guaranteed to find the first or the last value if the value to search for occurs multiple times. – Lasse V. Karlsen Jul 20 '14 at 20:25
  • @LasseV.Karlsen could you add the information from mike z about the internals of *why* this undefined behaviour actually behaves the way it does? I found it quite enlightening –  Jul 21 '14 at 00:54
  • 2
    To be honest, I'd rather not. You cannot rely on undocumented behavior and *that is my answer*. I'd rather not actually document the current behavior. It can stay in a comment. – Lasse V. Karlsen Jul 21 '14 at 07:04