Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Weird enum name resolution in C#

Tags:

c#

.net

enums

Consider the following code:

using System;

namespace Test
{
    enum Foo
    {
        A = 1,
        B = 1,
        C = 1
    }
    
    public static class Program
    {
        public static void Main()
        {
            Console.WriteLine("{0}, {1}, {2}", Foo.A, Foo.B, Foo.C);
        }
    }
}

Knowing that enums are just integers under the hood, I expected it to be either A, A, A or C, C, C. But surprisingly, it prints out B, B, B! This behaviour appears to be consistent across .NET Framework, .NET Core 3.x and .NET 5.

Why does it choose B?

like image 381
Impworks Avatar asked Apr 14 '21 12:04

Impworks


People also ask

Can two enums have the same name?

1. Two enum names can have same value. For example, in the following C program both 'Failed' and 'Freezed' have same value 0.

How do you find the enum of a string representation?

The Java Enum has two methods that retrieve that value of an enum constant, name() and toString(). The toString() method calls the name() method, which returns the string representation of the enum constant. In listing 1, the value returned by calling the name() and toString() on an Animal. DOG constant method is DOG.

Can you change the value of an enum in C?

You can change default values of enum elements during declaration (if necessary).

How do I find the enum of a name?

Enum. GetName(Type, Object) Method is used to get the name of the constant in the specified enumeration that has the specified value. Syntax: public static string GetName (Type enumType, object value);


1 Answers

It's undefined according to the documentation for Enum.GetName():

If multiple enumeration members have the same underlying value, the GetName method guarantees that it will return the name of one of those enumeration members. However, it does not guarantee that it will always return the name of the same enumeration member.

So it can do what it likes in this regard.

As to why it returns B in your example, we can inspect the implementation of GetEnumName():

public virtual string GetEnumName(object value)
{
    if (value == null)
        throw new ArgumentNullException("value");

    if (!IsEnum)
        throw new ArgumentException(Environment.GetResourceString("Arg_MustBeEnum"), "enumType");
    Contract.EndContractBlock();

    Type valueType = value.GetType();

    if (!(valueType.IsEnum || Type.IsIntegerType(valueType)))
        throw new ArgumentException(Environment.GetResourceString("Arg_MustBeEnumBaseTypeOrEnum"), "value");

    Array values = GetEnumRawConstantValues();
    int index = BinarySearch(values, value);

    if (index >= 0)
    {
        string[] names = GetEnumNames();
        return names[index];
    }

    return null;
}

Aha! All is explained. To make the lookup faster, they used a binary search. And where is the first place a binary search looks when starting the search? That's right - it starts halfway through the list. And that's why it's finding the B first - after the list is ordered, the B in in the middle.

(Note that the list is ordered by enum value, not enum name, so for your case the list is already ordered since all the values are the same.)

like image 137
Matthew Watson Avatar answered Oct 22 '22 08:10

Matthew Watson