2010-08-26 3 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 не является Enum. -или-значение является пустой строкой или содержит только пробел. -или- Значение - это имя, но не одна из именованных констант, определенных для перечисления.

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

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

+1

Возникает вопрос? –

+0

Почему вы спрашиваете и отвечаете на свой вопрос? –

+0

Вопрос о поведении ... – Markus

ответ

4

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

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

string value = "12345"; 

To:

string value = "Cake"; 

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

Придумайте Enum.Parse(enumType, value); делает следующее:

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

Вы правы, поскольку Enum является Int32 Enum.Parse ("<целочисленное представление>") является правильным при возврате действительного значения. Моя проблема была вызвана тем, что Enum был сжатым Int32, которого нет на самом деле. – Markus

+0

Это, строго говоря, не так. Строка никогда не «скрывается» для int, она требует преобразования, которое возможно с помощью различных методов ('Convert.ToInt32',' int.Parse', 'int.TryParse' и т. Д.). Более точное выражение 3.) будет «Является ли значение в' value' напрямую * конвертируемым * для базового типа (в данном случае 'Int32')? Если да, верните это значение и остановите (**, даже если нет Именованная константа для этого значения **). –

+0

@ Адам, справедливая точка, я изменил свой ответ соответственно =) – Rob

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

Он уже сказал, что в своем вопросе :) –

+0

Да, я знаю, но я не понимаю, почему Enum.Parse генерирует новые значения , это как вызов Int.Parse («большое число, неизвестное человечеству») и получение результата ... – Markus

0

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

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

+1

Неужели жаль, что 'Enum.Parse' принимает строковое представление числа?Я бы счел это сломанным, если бы это не так, потому что это означало бы, что значение перечисления не может быть округлено до строки и из строки, т. Е. «Enum.Parse (enumValue.ToString())» может выйти из строя. –

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 

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

Спасибо за дополнительную информацию о 'Enum.Parse()' и 'ToString()', я не знал об этой функции с запятыми. – Markus

Смежные вопросы