为什么Enum.Parse会创建未定义的条目?

时间:2010-08-26 12:50:41

标签: c# .net enums behavior

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
    }
}

如果执行上面的代码,结果显示:

  

你应该每天至少吃一个12345。

我真的希望在传递未知名称(字符串)时抛出ArgumentException。仔细查看Enum.Parse定义可以看出:

  

要点:
  将名称的字符串表示形式或一个或多个枚举常量的数值转换为等效的枚举对象。

     

例外:
   ArgumentException :enumType不是枚举。 - 或 - 值是空字符串或仅包含空格。 - 或 - value是一个名称,但不是为枚举定义的命名常量之一。

即。如果传递了整数的字符串表示,则会创建一个新的枚举值,现在设计会引发异常。这有意义吗?

至少我现在知道在Enum.IsDefined(enumType, value)

之前致电Enum.Parse()

4 个答案:

答案 0 :(得分:4)

“命名常量”是Enum值的文本表示,而不是您分配给它的数字。

如果你改变:

string value = "12345";

要:

string value = "Cake";

您将看到您期望的错误,因为“value是一个名称,但不是为枚举定义的命名常量之一。”。在这种情况下,您在中传递的值是名称“Cake”,但在枚举中不是。

考虑Enum.Parse(enumType, value);执行以下操作:

  1. 如果value是空引用,则抛出ArgumentNullException
  2. value中的值是enumType中枚举中指定常量之一。如果是,则从枚举中返回该值并停止。
  3. value中的值是否可以直接转换为基础类型(在本例中为Int32),如果是,则返回该值并停止(即使该值没有命名常量 )。
  4. value中的值是否可以直接转换为基础类型,但超出基础类型的范围?例如该值是一个包含大于MAXINT的数字的字符串。如果是,请抛出OverflowException
  5. 该值是否不能转换为基础类型?如果是,则抛出ArgumentException。

答案 1 :(得分:3)

枚举可以是其基本整数类型的任何值。它不仅限于命名常量。

例如,以下内容完全有效:

enum Foo{
    A,
    B,
    C,
    D
}

Foo x = (Foo)5;

即使5与命名常量不对应,它仍然是Foo的有效值,因为Foo的基础类型是Int32

如果要调用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);
}

答案 2 :(得分: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.

答案 3 :(得分:0)

我个人认为Enum.Parse接受数字的字符串表示很遗憾。如果您正在寻找替代方案,您可能希望查看具有各种解析选项的Unconstrained Melody项目,并且也是强类型的。

您当然可以Enum.IsDefined与解析结合使用。你肯定想要接受字符串版本的数字吗?或者你真的只期待名字?