Enum.Parse返回意外成员

时间:2017-07-04 08:19:07

标签: c#

考虑以下代码:

namespace ConsoleApplication1 {
class Program {
    public static void Main (string[] args) {
        var en = (TestEnum)Enum.Parse(typeof(TestEnum), "AA");
        Console.WriteLine(en.ToString());
        Console.ReadKey();
    }
}

public enum TestEnum {
    AA = 0x01,
    AB = 0x02, 
    AC = 0x03,
    BA = 0x01,
    BB = 0x02,
    BC = 0x03
}
}

如果执行此操作,变量en将获得TestEnum.BA的值。现在我已经从中学到了枚举标志应该是唯一的,或者你得到了这些意想不到的东西,但我确实不明白这里发生了什么。

更奇怪的部分是,当我将[Flags]属性添加到TestEnum时,它解决了问题并返回TestEnum.AA而不是TestEnum.BA,但是对于原始枚举(更大,大约~200)成员)我发现了这个问题,这没有什么区别。

我的理解是枚举是一种值类型,因此当你定义自己的标志时,它会将内存中的值存储为0x01(对于TestEnum.AA),当你将它从对象转换为TestEnum时,它会做查找该标志值并找到TestEnum.BA。

通过运行以下行也可以确认:

var en = (TestEnum)(object)TestEnum.AA;
Console.WriteLine(en.ToString());

将输出:BA

所以我的问题是:这到底发生了什么?更重要的是,为什么添加Flags属性会有所不同?

1 个答案:

答案 0 :(得分:12)

首先,这与Enum.Parse()无关。默认情况下,枚举的基础类型为int,因此在您的示例中TestEnum.AATestEnum.BA都存储为1,并且无法区分它们。

见以下代码:

Console.WriteLine(TestEnum.AA); // Prints BA
Console.WriteLine(TestEnum.BA); // Prints BA

其次,设置[Flags]属性更改输出的原因是因为在确定字符串时采用了不同的代码路径。

此处the code from ReferenceSource

private static String InternalFormat(RuntimeType eT, Object value)
{
    if (!eT.IsDefined(typeof(System.FlagsAttribute), false)) // Not marked with Flags attribute
    {
        // Try to see if its one of the enum values, then we return a String back else the value
        String retval = GetName(eT, value);
        if (retval == null)
            return value.ToString();
        else
            return retval;
    }
    else // These are flags OR'ed together (We treat everything as unsigned types)
    {
        return InternalFlagsFormat(eT, value);

    }
}

请注意,如果未设置GetName(),则会调用[Flags],否则会调用InternalFlagsFormat()

GetName()的实现最终会进行二元搜索以找到值,而InternalFlagsFormat()最终会进行线性搜索以查找值。

InternalFlagsFormat()必须进行线性搜索,因为它可能需要设置多个值(例如" X | Y | Z"),因此Microsoft为其实现了O(N)解决方案。然而,对于GetName(),他们寻求更有效的O(Log2(N))解决方案。

二元搜索可以(并且确实)找到与线性搜索不同的重复值,因此存在差异。