2010-08-26 17 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 
    } 
} 

Si se ejecuta el código anterior muestra el resultado:¿Por qué Enum.Parse crea entradas indefinidas?

Usted debe comer por lo menos un 12,345 por día.

Realmente esperaba que se lanzara una ArgumentException si se pasa un nombre desconocido (cadena). Tomando una mirada cercana a la definición Enum.Parse revela:

Resumen:
Convierte la representación de cadena del nombre o valor numérico de una o más constantes enumeradas a un objeto equivalente enumerado.

Excepciones:
ArgumentException: enumType no es una enumeración. -o-- value es una cadena vacía o solo contiene espacios en blanco. -o-- valor es un nombre, pero no una de las constantes con nombre definidas para la enumeración.

I.e. si se pasa una representación de cadena de un entero, se crea un nuevo valor enum y ahora se lanza una excepción por diseño. ¿Esto tiene sentido?

Al menos ahora sé que llamar Enum.IsDefined(enumType, value) antes Enum.Parse()

+1

¿Esto es una pregunta? –

+0

¿Por qué preguntas y respondes tu propia pregunta? –

+0

La pregunta es sobre el comportamiento ... – Markus

Respuesta

4

La "constante con nombre" es la representación textual del valor de Enum, no el número que le ha asignado.

Si cambia:

string value = "12345"; 

Para:

string value = "Cake"; 

Vas a ver el error que usted está esperando, porque "el valor es un nombre, pero no una de las constantes con nombre definido para la enumeración ". En este caso, el valor que está pasando en es, un nombre, "Pastel", pero no uno en la enumeración.

Piense en Enum.Parse(enumType, value); haciendo lo siguiente:

  1. Si value es una referencia nula, lanzar una ArgumentNullException
  2. Es el valor en value una de las constantes con nombre de la enumeración en enumType. En caso afirmativo, devuelva ese valor de la enumeración y deténgalo.
  3. Es el valor en value directamente convertible al tipo subyacente (en este caso Int32), si es así, devuelva ese valor y pare (incluso si no hay una constante con nombre para ese valor).
  4. ¿Es el valor en value directamente convertible al tipo subyacente, pero fuera del rango del tipo subyacente? p.ej. el valor es una cadena que contiene un número uno mayor que MAXINT. Si es así, arroja un OverflowException.
  5. ¿El valor no es moldeable para el tipo subyacente? Si es así, lanza una ArgumentException.
+0

Tiene razón, ya que Enum es Int32 Enum.Parse ("") es correcto al devolver un valor válido. Mi problema fue causado al ver a Enum como un Int32 constreñido que de hecho no lo es. – Markus

+0

Esto no es estrictamente correcto. Una cadena nunca es "moldeable" para una int, requiere una conversión que es posible a través de una variedad de métodos ('Convert.ToInt32',' int.Parse', 'int.TryParse', etc.). Una expresión más precisa de 3.) sería "¿Es el valor en' valor' directamente * convertible * al tipo subyacente (en este caso 'Int32')? En caso afirmativo, devuelva ese valor y detenga (** incluso si no hay llamada constante para ese valor **). –

+0

@ Adam, punto justo, he cambiado mi respuesta en consecuencia =) – Rob

0

Es necesario utilizar 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

Él ya dijo eso en su pregunta :) –

+0

Sí, lo sé, pero no entiendo por qué Enum.Parse genera nuevos valores , es como llamar a Int.Parse ("gran número desconocido para la humanidad") y obtener un resultado ... – Markus

0

yo personalmente creo que es una lástima que Enum.Parse acepta la cuerda representación de un número. Si está buscando una alternativa, puede consultar mi proyecto Unconstrained Melody, que tiene varias opciones de análisis, y también está fuertemente tipado.

Ciertamente puede use Enum.IsDefined junto con el análisis. ¿Definitivamente usted quiere para aceptar versiones de cadena de números? ¿O realmente solo estás esperando nombres?

+1

¿Es realmente una pena que 'Enum.Parse' acepte la representación de cadena de un número?Lo consideraría roto si no fuera así, porque significaría que un valor de enum podría no tener un viaje de ida y vuelta a una cadena, es decir, 'Enum.Parse (enumValue.ToString())' podría fallar. –

3

Un enum puede ser cualquier valor de su tipo de entero base. No está solo restringido a constantes con nombre.

Por ejemplo, el siguiente es perfectamente válido:

enum Foo{ 
    A, 
    B, 
    C, 
    D 
} 

Foo x = (Foo)5; 

5 A pesar de que no corresponde a una constante con nombre, todavía es un valor válido para Foo, ya que el tipo subyacente de Foo es Int32.

Si se llamara x.ToString(), el valor devuelto sería simplemente "5", ya que ninguna constante con nombre corresponde con el valor de x.

Enum.Parse() es la función inversa de Enum.ToString(). Debería esperar que cualquiera que sea Enum.ToString() puede devolver que Enum.Parse() puede aceptar. Esto incluye, por ejemplo, valores separados por comas de banderas enumeraciones:

[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); 

Salida:

 
15 
A, B, C, D 
True 
True 

EDIT:

Lo que realmente parece querer decir algo como esto:

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

o una versión sin mayúsculas y minúsculas:

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

Gracias por la información adicional sobre 'Enum.Parse()' y 'ToString()', no sabía sobre la característica con valores separados por comas. – Markus

Cuestiones relacionadas