2010-08-26 20 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