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

ser*_*dat 58 .net c# enums

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

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

主要方法:

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

第一次尝试:

enum Test
{
    a1=0,
    a2=0,
    a3=0,
    a4=0,
}
Run Code Online (Sandbox Code Playgroud)

输出:

a2
Run Code Online (Sandbox Code Playgroud)

第二次尝试:

enum Test
{
    a1=0,
    a2=0,
    a3,
    a4=0,
}
Run Code Online (Sandbox Code Playgroud)

输出:

a4
Run Code Online (Sandbox Code Playgroud)

第三次尝试:

enum Test
{
    a1=0,
    a2=0,
    a3,
    a4,
}
Run Code Online (Sandbox Code Playgroud)

输出:

a2
Run Code Online (Sandbox Code Playgroud)

第四次尝试:

enum Test
{
    a1=0,
    a2=0,
    a3,
    a4
}
Run Code Online (Sandbox Code Playgroud)

输出:

a1
Run Code Online (Sandbox Code Playgroud)

D S*_*ley 68

文件实际上解决了这个:

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

(重点补充)

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

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

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

  • 一些行为会使外观具有确定性.它们可能在99.9%的情况下表现完全相同.它们甚至可能是*确定性的,并且在100%的时间内表现相同.但它并不能保证永远是可预测的.最好不要依赖未记录的行为,特别是当有关于行为的明确警告时. (30认同)
  • 与"只是编译器当前如何实现"的逻辑不同.MSDN告诉您,您推断的任何逻辑都不会被信任或依赖. (5认同)
  • 嗯,是.但输出并非随机.例如,第四个的输出总是a1.所以这背后必须有一个逻辑. (3认同)
  • @ParasDPain我在答案中链接到它. (2认同)
  • @jackjop - 根据实现,理论上它也可能会随着每次编译而改变.每个构建将始终检索相同的值,但下一个构建将是不同的.不知道什么样的实现会这样做,但规范允许. (2认同)
  • @jackjop更有可能在六年后新编译器改变它"总是"所做的事情,现在你的代码充满了bug,因为你忽略了警告. (2认同)

Xia*_*312 28

TL; DR:枚举的所有字段将通过反射提取,然后插入排序并二进制搜索第一个匹配值.


调用链看起来像这样:

Enum.Tostring();
Enum.InternalFormat(RuntimeType eT, Object value);
Enum.GetName(Type enumType, Object value);
Type.GetEnumName(object value);
Run Code Online (Sandbox Code Playgroud)

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;
    }
Run Code Online (Sandbox Code Playgroud)

双方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;
    }
Run Code Online (Sandbox Code Playgroud)

如果遵循,GetFields(BindingFlags bindingAttr)导致一个abstract方法,但在msdn上搜索"GetFields"将产生你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...
Run Code Online (Sandbox Code Playgroud)

所以,我会引用一些Type.GetFields评论:

GetFields方法不按特定顺序返回字段,例如按字母顺序或声明顺序.您的代码不得依赖于返回字段的顺序,因为该顺序会有所不同.

  • 这就是它的实现方式.没有任何东西可以保证它;在所有版本,所有平台或所有编译器中以相同的方式实现. (16认同)
  • 如果一切都失败了,请阅读手册.如果那也失败了 - 请阅读来源 (14认同)
  • 有趣的是看到它在运行时有多贵.我曾假设编译器会在编译时解决它,显然不是 (2认同)
  • 当 Oracle 数据库版本 10 出来时,他们做了一些内部优化,其中没有“order by”排序子句但有 group by 子句的 SQL 查询不再总是按 group by 列排序。直到第 9 版,它也会按组排序,但这不能保证,而且在升级到 Oracle 10 时很多代码都炸了。我知道我的一些人做到了! (2认同)