2017-07-04 67 views
9

考慮下面的代碼:Enum.Parse返回意外會員

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

由於他們有相同的價值,它會挑一個漂亮的任意。 [這個相關的問題](https://stackoverflow.com/questions/8043027/non-unique-enum-values)可能會有所幫助。 –

+0

我不知道枚舉的內部工作原理,所以我不會在這裏回答(會有更多有能力的人可以做到這一點),但作爲一個事實,'[Flags]'屬性,** only **對'ToString()'部分(格式化)有任何重要性......否則,枚舉的工作原理完全相同 – Jcl

+1

添加'[Flags]'可能會改變用於確定正確的算法值。兩個答案其實都是正確的。 – DavidG

回答

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))解決方案。

二進制搜索可以(並且確實)找到與線性搜索不同的重複值,因此是差異。

+0

感謝您提供非常全面的答案,並查找了Enum源代碼。 +1 – larzz11