考虑以下代码:
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属性会有所不同?
答案 0 :(得分:12)
首先,这与Enum.Parse()
无关。默认情况下,枚举的基础类型为int
,因此在您的示例中TestEnum.AA
和TestEnum.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))解决方案。
二元搜索可以(并且确实)找到与线性搜索不同的重复值,因此存在差异。