2010-08-26 58 views
8
class Program 
{ 
    static void Main(string[] args) 
    { 
     string value = "12345"; 
     Type enumType = typeof(Fruits); 
     Fruits fruit = Fruits.Apple; 
     try 
     { 
      fruit = (Fruits) Enum.Parse(enumType, value); 
     } 
     catch (ArgumentException) 
     { 
      Console.WriteLine(String.Format("{0} is no healthy food.", value)); 
     } 
     Console.WriteLine(String.Format("You should eat at least one {0} per day.", fruit)); 
     Console.ReadKey(); 
    } 

    public enum Fruits 
    { 
     Apple, 
     Banana, 
     Orange 
    } 
} 

如果執行結果上面的代碼所示:爲什麼Enum.Parse會創建未定義的條目?

你應該每天吃至少一個12345。

如果傳遞一個未知的名字(字符串),我真的希望拋出一個ArgumentException異常。在Enum.Parse的定義以近距離觀察,可以發現:

摘要:
名稱或數值的一個或多個枚舉常量爲等效枚舉對象的字符串表示形式。

例外:
的ArgumentException:enumType不是一個枚舉。 - 或 - 值是一個空字符串或只包含空格。 - 或 - 值是名稱,但不是爲枚舉定義的命名常量之一。

I.e.如果傳遞一個整數的字符串表示形式,則會創建一個新的枚舉值,現在,設計會引發異常。這有意義嗎?

至少我現在知道叫Enum.IsDefined(enumType, value)之前Enum.Parse()

+1

這是一個問題? – 2010-08-26 12:54:01

+0

你爲什麼要問和回答你自己的問題? – 2010-08-26 12:54:25

+0

問題的行爲被問到...... – Markus 2010-08-26 13:00:44

回答

4

「命名常量」是Enum值的文本表示,而不是您分配給它的數字。

如果更改:

string value = "12345"; 

要:

string value = "Cake"; 

你會看到你期待的錯誤,因爲「值是出了名的,但沒有定義命名常量之一列舉。「。在這種情況下,您在中傳遞的值是名稱「Cake」,但枚舉中沒有一個。

Enum.Parse(enumType, value);想想做以下幾點:

  1. 如果value爲空引用,拋出一個ArgumentNullException
  2. 是在在enumType枚舉命名常量的value一個值。如果是,則從枚舉中返回該值並停止。
  3. value中的值是否可以直接轉換爲基礎類型(在本例中爲Int32),如果是,則返回該值並停止(即使對於該值沒有命名常量,也返回)。
  4. 是否可以將value中的值直接轉換爲基礎類型,但是不在基礎類型的範圍之內?例如該值是一個包含大於MAXINT的數字的字符串。如果是的話,拋出一個OverflowException
  5. 該值不可轉換爲基礎類型嗎?如果是,請拋出一個ArgumentException。
+0

你是對的,因爲Enum是一個Int32 Enum.Parse(「」)在返回一個有效值時是正確的。我的問題是由於將Enum看作實際上不是Int32的收縮Int32而引起的。 – Markus 2010-08-26 13:28:47

+0

嚴格來說,這不是正確的。一個字符串從不「可轉換」爲int,它需要通過各種方法('Convert.ToInt32','int.Parse','int.TryParse'等)進行轉換。 3.)的更準確的表達式是:「value中的值是否可以直接轉換爲基礎類型(在本例中爲Int32)?如果是,則返回該值並停止(即使沒有 – 2010-08-26 13:44:35

+0

@亞當,公平點,我已經改變了我的答案相應=) – Rob 2010-08-26 13:52:17

0

您需要使用Enum.IsDefined:

http://msdn.microsoft.com/en-us/library/essfb559.aspx

using System; 

    [Flags] enum Colors { None=0, Red = 1, Green = 2, Blue = 4 }; 

    public class Example 
    { 
     public static void Main() 
     { 
      string[] colorStrings = { "0", "2", "8", "blue", "Blue", "Yellow", "Red, Green" }; 
      foreach (string colorString in colorStrings) 
      { 
      try { 
       Colors colorValue = (Colors) Enum.Parse(typeof(Colors), colorString);   
       if (Enum.IsDefined(typeof(Colors), colorValue) | colorValue.ToString().Contains(",")) 
        Console.WriteLine("Converted '{0}' to {1}.", colorString, colorValue.ToString()); 
       else 
        Console.WriteLine("{0} is not an underlying value of the Colors enumeration.", colorString); 
      } 
      catch (ArgumentException) { 
       Console.WriteLine("'{0}' is not a member of the Colors enumeration.", colorString); 
      } 
      } 
     } 
    } 
    // The example displays the following output: 
    //  Converted '0' to None. 
    //  Converted '2' to Green. 
    //  8 is not an underlying value of the Colors enumeration. 
    //  'blue' is not a member of the Colors enumeration. 
    //  Converted 'Blue' to Blue. 
    //  'Yellow' is not a member of the Colors enumeration. 
    //  Converted 'Red, Green' to Red, Green. 
+0

他已經說過,在他的問題:) – 2010-08-26 12:55:17

+0

是的我知道,但我不明白爲什麼Enum.Parse生成新的值,就像調用Int.Parse(「人類不知道的大數字」)並得到結果... – Markus 2010-08-26 12:57:15

0

我個人認爲這是一個遺憾,Enum.Parse接受的字符串一個數字的表示。如果您正在尋找替代方案,您可能需要查看我的Unconstrained Melody項目,該項目有各種解析選項,並且也是強類型的。

您當然可以可以結合使用Enum.IsDefined解析。你確定想要接受字符串版本的數字?或者你真的只是在期待名字?

+1

Enum.Parse接受數字的字符串表示形式真的很遺憾嗎?如果不這樣做,我會認爲它會被破壞,因爲這意味着枚舉值可能不會往返於一個字符串,即'Enum.Parse(enumValue.ToString())'可能會失敗。 – 2010-08-26 13:37:29

3

enum可以是其基本整數類型的任何值。它不僅限於命名常量。

例如,以下是完全有效的:

enum Foo{ 
    A, 
    B, 
    C, 
    D 
} 

Foo x = (Foo)5; 

即使5不對應於給定常數,它仍然是Foo一個有效值,因爲底層類型FooInt32

如果有人打電話給x.ToString(),返回的值將只是「5」,因爲沒有命名常量與x的值相對應。

Enum.Parse()Enum.ToString()的逆向功能。你應該可以期待Enum.ToString()可以返回,Enum.Parse()可以接受。這包括,例如,逗號分隔值標誌枚舉:

[Flags] 
enum Foo{ 
    A = 1, 
    B = 2, 
    C = 4, 
    D = 8 
} 

Foo x = Foo.A | Foo.B | Foo.C | Foo.D; 
int i = (int)x; 
string s = x.ToString(); 
Console.WriteLine(i); 
Console.WriteLine(s); 
Console.WriteLine((Foo)Enum.Parse(typeof(Foo), i.ToString()) == x); 
Console.WriteLine((Foo)Enum.Parse(typeof(Foo), s) == x); 

輸出:

 
15 
A, B, C, D 
True 
True 

編輯:

你真正似乎想是這樣的:

static Enum GetEnumValue(Type enumType, string name){ 
    // null-checking omitted for brevity 

    int index = Array.IndexOf(Enum.GetNames(enumType), name); 
    if(index < 0) 
     throw new ArgumentException("\"" + name + "\" is not a value in " + enumType, "name"); 

    return Enum.GetValues(enumType).GetValue(index); 
} 

或不區分大小寫的版本:

static Enum GetEnumValue(Type enumType, string name, bool ignoreCase){ 
    // null-checking omitted 

    int index; 
    if(ignoreCase) 
     index = Array.FindIndex(Enum.GetNames(enumType), 
      s => string.Compare(s, name, StringComparison.OrdinalIgnoreCase) == 0); 
      // or StringComparison.CurrentCultureIgnoreCase or something if you 
      // need to support fancy Unicode names 
    else index = Array.IndexOf(Enum.GetNames(enumType), name); 

    if(index < 0) 
     throw new ArgumentException("\"" + name + "\" is not a value in " + enumType, "name"); 

    return Enum.GetValues(enumType).GetValue(index); 
} 
+0

感謝您對Enum.Parse()和ToString()的更多見解,我不知道關於使用逗號分隔值的功能。 – Markus 2010-08-26 13:55:08