如果Enum值相同,我将获得哪个枚举常量

时间:2016-05-16 20:55:57

标签: c# .net enums

如果有多个具有相同值的枚举常量,是否存在一个常数我得到的逻辑?

我尝试了下面的变化,但无法得到合理的逻辑。

主要方法:

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 个答案:

答案 0 :(得分:68)

documentation实际上解决了这个问题:

  

如果多个枚举成员具有相同的基础值,并且您尝试根据其基础值检索枚举成员名称的字符串表示形式,您的代码不应对该方法将返回的名称做出任何假设

(强调补充)

但是,这并不意味着结果是 random 。这意味着它是一个可能会发生变化的实现细节。只需一个补丁就可以完全改变实现,在编译器(MONO,Roslyn等)之间可能会有所不同,并且在不同的平台上会有所不同。

如果您的系统设计为需要,则枚举的反向查找在时间和平台上是一致的,那么不会使用 Enum.ToString。要么改变你的设计,要么它不依赖于那个细节,要么编写自己的方法保持一致。

因此,您不应编写依赖于该实现的代码,否则您将承担在将来的版本中不知情的情况下会发生更改的风险。

答案 1 :(得分:28)

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方法,但会搜索&#34; GetFields&#34;在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方法不会按特定顺序返回字段,例如按字母顺序或声明顺序。您的代码不得依赖于返回字段的顺序,因为该顺序会有所不同。