Почему Enum.Parse создает неопределенные записи? - PullRequest
8 голосов
/ 26 августа 2010
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 не является Enum.-или- значение либо пустая строка, либо содержит только пробел.-или- значение - это имя, но не одна из именованных констант, определенных для перечисления .

Т.е., если передается строковое представление целого числа, создается новое значение перечисления, а теперь исключение выдается при разработке.Имеет ли это смысл?

По крайней мере, теперь я знаю, чтобы позвонить Enum.IsDefined(enumType, value) до Enum.Parse()

Ответы [ 4 ]

4 голосов
/ 26 августа 2010

«Именованная константа» - это текстовое представление значения Enum, а не числа, которое вы ему присвоили.

Если вы измените:

string value = "12345";

На:

string value = "Cake";

Вы увидите ожидаемую ошибку, потому что «значение - это имя, но не одна из именованных констант, определенных для перечисления.».В данном случае значение, которое вы передаете в , равно имени, "Cake", но не одному в перечислении.

Представьте, что Enum.Parse(enumType, value); делает следующее:

  1. Если value является пустой ссылкой, выдается исключение ArgumentNullException
  2. Является ли значение в value одной из именованных констант в перечислении в enumType.Если да, верните это значение из перечисления и остановите.
  3. Является ли значение в value, непосредственно конвертируемым в базовый тип (в данном случае Int32), если да, верните это значение и остановите (даже если для этого значения нет именованной константы ).
  4. Может ли значение в value быть непосредственно преобразованным в базовый тип, но вне диапазона базового типа?Например, значение является строкой, содержащей число на единицу больше, чем MAXINT.Если да, выведите OverflowException.
  5. . Можно ли преобразовать значение в базовый тип?Если да, выведите ArgumentException.
3 голосов
/ 26 августа 2010

Перечисление может быть любым значением его базового целочисленного типа. Он не ограничен только именованными константами.

Например, следующее совершенно правильно:

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

EDIT:

То, что вы действительно хотите, выглядит примерно так:

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 голосов
/ 26 августа 2010

Лично мне жаль, что Enum.Parse принимает строковое представление числа. Если вы ищете альтернативу, вы можете взглянуть на мой проект Unconstrained Melody , который имеет различные параметры синтаксического анализа и строго типизирован.

Вы, конечно, можете использовать Enum.IsDefined в сочетании с анализом. Вы определенно хотите принимать строковые версии чисел, хотя? Или вы действительно ожидаете только имена?

0 голосов
/ 26 августа 2010

Вам необходимо использовать 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.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...