Какую константу перечисления я получу, если значения перечисления одинаковы


есть ли логика, к которой константа я получаю, если есть более одной константы перечисления, которая имеет то же значение?

я попробовал варианты ниже, но не смог получить разумную логику.

Основной Метод:

public class Program
{
    public static void Main(string[] args)
    {
        Test a = 0;
        Console.WriteLine(a);
    }
}

первый вариант:

enum Test
{
    a1=0,
    a2=0,
    a3=0,
    a4=0,
}

выход:

a2

вторая попытка:

enum Test
{
    a1=0,
    a2=0,
    a3,
    a4=0,
}

выход:

a4

третий попробуйте:

enum Test
{
    a1=0,
    a2=0,
    a3,
    a4,
}

выход:

a2

четвертая попытка:

enum Test
{
    a1=0,
    a2=0,
    a3,
    a4
}

выход:

a1
2 58

2 ответа:

на документация фактически обращается к этому:

если несколько членов перечисления имеют одно и то же базовое значение и вы пытаетесь получить строковое представление имени члена перечисления на основе его базового значения, код не должен делать никаких предположений о том, какое имя метод возвращает.

(курсив)

однако, это не означает, что результат случайные. Что это значит, что это деталь реализации, которая может измениться. Реализация может полностью измениться с помощью всего лишь патча, может быть разной для разных компиляторов (MONO, Roslyn и т. д.), и быть разными на разных платформах.

Если ваша система разработана, что это требует что обратный поиск для перечислений согласован с течением времени и платформ, то не используйтеEnum.ToString. Либо изменить ваш дизайн, так что это не зависит от этой детали, или написать свой собственные метод будет будьте последовательны.

поэтому вы не должны писать код, который зависит от этой реализации, или вы рискуете, что он изменится без вашего ведома в будущем выпуске.

TL; DR: все поля перечисления будут извлечены отражением, затем вставка отсортирована и двоичный поиск первого совпадающего значения.


цепочка вызовов выглядела так:

Enum.Tostring();
Enum.InternalFormat(RuntimeType eT, Object value);
Enum.GetName(Type enumType, Object value);
Type.GetEnumName(object value);

Type.GetEnumName(object value) реализуется так :

    public virtual string GetEnumName(object value)
    {
        // standard argument guards...

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

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

        return null;
    }

и GetEnumRawConstantValues() и GetEnumNames() полагаются на GetEnumData(out string[] enumNames, out Array enumValues):

    private void GetEnumData(out string[] enumNames, out Array enumValues)
    {
        Contract.Ensures(Contract.ValueAtReturn<String[]>(out enumNames) != null);
        Contract.Ensures(Contract.ValueAtReturn<Array>(out enumValues) != null);

        FieldInfo[] flds = GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);

        object[] values = new object[flds.Length];
        string[] names = new string[flds.Length];

        for (int i = 0; i < flds.Length; i++)
        {
            names[i] = flds[i].Name;
            values[i] = flds[i].GetRawConstantValue();
        }

        // Insertion Sort these values in ascending order.
        // We use this O(n^2) algorithm, but it turns out that most of the time the elements are already in sorted order and
        // the common case performance will be faster than quick sorting this.
        IComparer comparer = Comparer.Default;
        for (int i = 1; i < values.Length; i++)
        {
            int j = i;
            string tempStr = names[i];
            object val = values[i];
            bool exchanged = false;

            // Since the elements are sorted we only need to do one comparision, we keep the check for j inside the loop.
            while (comparer.Compare(values[j - 1], val) > 0)
            {
                names[j] = names[j - 1];
                values[j] = values[j - 1];
                j--;
                exchanged = true;
                if (j == 0)
                    break;
            }

            if (exchanged)
            {
                names[j] = tempStr;
                values[j] = val;
            }
        }

        enumNames = names;
        enumValues = values;
    }

затем GetFields(BindingFlags bindingAttr) приводит к abstract метод, но поиск "GetFields" на msdn даст вам EnumBuilder.GetFields(BindingFlags bindingAttr). И если мы будем следовать его цепочке вызовов:

EnumBuilder.GetFields(BindingFlags bindingAttr);
TypeBuilder.GetFields(BindingFlags bindingAttr);
RuntimeType.GetFields(BindingFlags bindingAttr);
RuntimeType.GetFieldCandidates(String name, BindingFlags bindingAttr, bool allowPrefixLookup);
RuntimeTypeCache.GetFieldList(MemberListType listType, string name);
RuntimeTypeCache.GetMemberList<RuntimeFieldInfo>(ref MemberInfoCache<T> m_cache, MemberListType listType, string name, CacheType cacheType);
MemberInfoCache<RuntimeFieldInfo>.GetMemberList(MemberListType listType, string name, CacheType cacheType);
MemberInfoCache<RuntimeFieldInfo>.Populate(string name, MemberListType listType, CacheType cacheType);
MemberInfoCache<RuntimeFieldInfo>.GetListByName(char* pName, int cNameLen, byte* pUtf8Name, int cUtf8Name, MemberListType listType, CacheType cacheType);
MemberInfoCache<RuntimeFieldInfo>.PopulateFields(Filter filter);
// and from here, it is a wild ride...

Итак, я процитирую Type.GetFields Примечания :

метод GetFields не возвращает поля в определенном порядке, например по алфавиту или в порядке объявления. Ваш код не должен зависеть от порядка, в котором поля возвращаются, так как этот порядок меняется.