2011-09-21 11 views
10

Para convertir una cadena en una enumeración, ¿cuál de las siguientes formas es mejor?Enum.Parse() o Switch

  1. este código:

    colorEnum color = (colorEnum)Enum.Parse(typeof(colorEnum), "Green"); 
    
  2. o esto:

    string colorString = ... 
    colorEnum color;   
    switch (colorString) 
    { 
        case "Green": 
         color = colorEnum.Green; 
         break; 
        case "Red": 
         color = colorEnum.Red; 
         break; 
        case "Orange": 
         color = colorEnum.Orange; 
         break; 
        .... 
    } 
    
+0

Pensé que no podía hacer el cambio en las cadenas. –

+4

@Ramon: Te equivocas: en C# siempre hemos sido capaces de activar las cadenas. –

+0

@Ramon: Por supuesto que puedes. –

Respuesta

8

Se debe utilizar la Enum.TryParse, si no se puede controlar el error correctamente.

muestra:

 ColorsEnum colorValue; 
    if (Enum.TryParse(colorString, out colorValue))   
     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); 
    else 
     Console.WriteLine("{0} is not a member of the Colors enumeration.", colorString); 
+0

Ten en cuenta que Enum.TryParse solo está disponible en .Net 4.0 –

+2

Y hay otras desventajas de Enum.TryParse - mira mi responder. –

2

1) es mucho mejor. Es un código más limpio. Estás haciendo en una línea lo que tomaría múltiples en 2). Además, es menos propenso a errores. Cuando agrega otro elemento a colorEnum, debe recordar ampliar 2) cuando 1) simplemente funcione.

Es posible que también desee un cierto control de errores en el Enum.Parse.

+1

y luego alguien decide darle la cadena "0" ... – harold

4

¿Y qué pasa con Enum.TryParse<TEnum>?

string myColorStr = "red"; 
colorEnum myColor; 
if(!Enum.TryParse<colorEnum>(myColorStr, true, out myColor)) 
{ 
    throw new InvalidOperationException("Unknown color " + myColorStr); 
} 
7

(Advertencia: incluye enchufe para mi propia biblioteca de código abierto ...)

Personalmente me gustaría usar Unconstrained Melody, que termina con más limpio y más código de seguridad de tipos:

ColorEnum color = Enums.ParseName<ColorEnum>(text); 

Puede usar TryParseName si sospecha que puede no ser válido. Obviamente, esto requiere una biblioteca adicional, pero esperamos que pueda encontrar otras cosas útiles allí también :)

Enum.TryParse de .NET 4 es mejor que el otro incorporada en las opciones, pero:

  • Ganaste no captura tipos que no son enum en tiempo de compilación, por ejemplo Enum.TryParse<int>(...) aún compilará; Melody sin restricciones realmente sólo permite tipos de enumeración
  • Enum.TryParse también analizar "1" (o cualquiera que sea el valor numérico es cuando se convierte en una cadena) - si es que realmente sólo espera nombres, creo que es mejor solamente aceptar nombres

definitivamente ¿no interruptor en los valores de cadena - que significa si cambia el nombre de los valores de enumeración, usted tiene que recordar para cambiar el nombre del valor del caso así.

+0

+1 Greate one. –

+0

Dado que todos los métodos en Enums.cs están restringidos de la misma manera, ¿no podría expresarlo como una restricción de clase en su lugar (y poder nombrarlo 'Enum '), o eso no funciona (o hay alguna razón por la que evitando eso)? –

+0

@Damien: Bueno, los métodos de extensión no pueden estar en una clase genérica. I * podría * potencialmente tener una clase para métodos de extensión y una clase para métodos que no sean de extensión ... pero no estoy seguro de que suponga una gran diferencia. –

1

Aparte del hecho de que los dos fragmentos de código diferente no hacen lo mismo, me gustaría utilizar este:

colorEnum color; 
if (!colorEnum.TryParse(colorString, true, out color) 
    color = colorEnum.Green; // Or whatever default value you wish to have. 

Si usted no tiene .NET 4.0, entonces me gustaría hacer algo como esto:

public static TEnum ToEnum<TEnum>(this string strEnumValue, TEnum defaultValue) 
{ 
    if (!Enum.IsDefined(typeof(TEnum), strEnumValue)) 
     return defaultValue; 

    return (TEnum)Enum.Parse(typeof(TEnum), strEnumValue); 
} 

Esta es una Extension Method-string.

0

La variante de cambio me parece horrible ya que tendrá que modificar el interruptor cada vez que cambie la enumeración también.

Me gusta usar el TryParse que pertenece a su enumeración. Así que usted puede utilizar de esta manera

string colorString = ..... 
colorEnum color; 

colorEnum.TryParse(colorString, out color); 

O si no se preocupan por el caso de la cadena

colorEnum.TryParse(colorString, true, out color); 

El returnvalue de TryParse es cierto si la cadena era una enumeración válida, si es falsa no.

2

Número 1 simplemente en legibilidad y facilidad de mantenimiento. Si extiende la enumeración no necesita hacer ningún trabajo adicional, mientras que con 2 tiene que agregar más casos a la instrucción switch

0

Desde el punto de vista del rendimiento, como las enumeraciones se implementan como campos estáticos, el método de análisis probablemente termina haciendo una reflexión sobre el tipo enum e intenta con un método GetField que podría ser más rápido que el caso. En el otro extremo, si el 90% del caso, el color es verde, el caso será muy rápido ... Tenga en cuenta que el CLR a veces reorganiza el código internamente, cambiando el orden del caso en función de la estadística (en realidad, estoy no estoy seguro de que lo haga, pero el doctor dice que sí).

+3

por el contrario, la reflexión es lenta ya que las melazas y el interruptor son extremadamente rápidos: activar cadenas se implementa como un diccionario para convertir cadenas a enteros y luego un conmutador denso normal, que no tiene nada que ver con estadísticas porque no es una tabla de salto "IF lineales" que * no se usa de todos modos *, el compilador crea un "árbol de FI y algunos modificadores para las partes densas", que es, por supuesto, un detalle de implementación – harold

+0

@harold: gracias – VdesmedT

2

Como ha agregado la etiqueta 'rendimiento', voy a usar el interruptor.
Sí, tendrá que cambiar los casos cuando renombre/agregue/elimine cualquier cosa en la enumeración. Bueno, eso es una lástima entonces. Cualquier variante de Enum.Parse/TryParse utiliza una gran cantidad de código extraño y algo de reflexión, solo eche un vistazo dentro de la función con ILSpy u otros. Luego también está la cuestión de aceptar "-12354" e incluso una lista de nombres válidos separados por comas (lo que da como resultado que todos ellos estén OR juntos) incluso cuando la enumeración no tenga un atributo [Flags].

Como alternativa, puede crear un diccionario que traduzca los nombres enum a valores. En realidad, debería ser más rápido que el interruptor, porque un interruptor en las cadenas también pasa por un diccionario, pero guarda la parte del interruptor real.

Obviamente, ambas formas cuestan un poco más de mantenimiento que enum.parse y variantes; si vale la pena es hasta , ya que de todos nosotros solo usted tiene suficiente conocimiento del proyecto para hacer la transacción de rendimiento/tiempo de codificación.

0

Utilizo lo siguiente, le ofrece todo el tipo de seguridad sin recaer cuando agrega nuevos valores al Enum, también es muy rápido.

public static colorEnum? GetColorFromString(string colorString) 
{ 
    colorEnum? retVal = null; 
    if(Enum.IsDefined(typeof(colorEnum), colorString)) 
     retVal = (colorEnum)Enum.Parse(typeof(colorEnum), colorString); 
    return retVal; 
} 

Mi prueba con 8 elementos de la enumeración muestra de esta manera ser más rápido que el método de cambio.

o bien puede utilizar (la forma muy lenta):

public static colorEnum? GetColorFromString(string colorString) 
{ 
    foreach (colorEnum col in Enum.GetValues(typeof(colorEnum))) 
    { 
     if (col.ToString().Equals(colorString)) 
     { 
      return col; 
     } 
    } 
    return null; 
} 
1

Personalmente, mientras estoy totalmente bien con la solución Enum.Parse para escenarios de irregularidad (es decir: uno despegue de esta función de vez en cuando ... y hay muchos escenarios de este tipo para estar seguros), no puedo tolerar la posibilidad de que se involucre algún método de tipo de reflexión cuando esta función se debe realizar en un bucle sobre cientos/miles más valores enum a la vez.Gack!

Así que la siguiente es una solución que obtiene lo mejor de ambos mundos.

Simplemente recupere todos los valores de la enumeración en el momento del inicio o lo que no, siempre que le parezca mejor (a continuación hay una forma de hacerlo), y luego construya un diccionario con ellos.

private static Dictionary<string, Color> colorDictionary; 
    public static Dictionary<string, Color> ColorDictionary 
    { 
     get 
     { 
      if (colorDictionary== null) { 
       colorDictionary = new Dictionary<string, Color>(); 
       var all = Enum.GetValues(typeof(Color)).OfType<Color>(); 
       foreach (var val in all) 
        dict.Add(val.ToString(), val); 
      } 
      return colorDictionary; 
     } 
    }