2011-09-21 2 views
10

Per convertire una stringa in enum, quale dei seguenti modi è migliore?Enum.Parse() o Switch

  1. Questo codice:

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

    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

Ho pensato che non è possibile attivare le stringhe. –

+4

@Ramon: ti sbagli: in C# siamo sempre stati in grado di attivare le stringhe. –

+0

@ Ramon: certo che puoi. –

risposta

8

Si dovrebbe usare l'Enum.TryParse, se non riesce è possibile gestire in modo corretto l'errore.

campione:

 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

Nota che Enum.TryParse è disponibile solo su .Net 4.0 –

+2

E ci sono altri aspetti negativi di Enum.TryParse - guarda il mio risposta. –

2

1) è molto meglio. È un codice più pulito. Stai facendo in una riga ciò che richiederebbe più di 2). Inoltre, è meno incline agli insetti. Quando aggiungi un altro articolo a colorEnum, dovresti ricordarti di estendere 2) dove 1) funzionerebbe.

Si consiglia inoltre di gestire alcuni errori su Enum.Parse.

+1

e poi qualcuno decide di fornirti la stringa "0" .. – harold

4

E che dire di Enum.TryParse<TEnum>?

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

(Attenzione: include presa per la mia libreria open source ...)

Personalmente userei Unconstrained Melody, che finisce con il più pulito e più codice typesafe:

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

Puoi utilizzare TryParseName se ritieni che possa essere non valido. Ovviamente questo richiede una libreria in più, ma si spera che troverete altre cose utili in là troppo :)

Enum.TryParse da .NET 4 è migliore rispetto agli altri built-in opzioni, ma:

  • Hai vinto 'catturare i tipi non enumerici in fase di compilazione, es Enum.TryParse<int>(...) sarà ancora compilato; Senza vincoli Melody davvero consente solo tipi enum
  • Enum.TryParse potranno anche analizzare "1" (o qualunque sia il valore numerico è quando convertito in una stringa) - se è davvero solo aspetta nomi, penso che sia meglio solo accettare nomi

io sicuramente avrei non accendere i valori di stringa - vuol dire se si rinomina i valori enum, hai avuto modo di ricordare di rinominare il valore caso.

+0

+1 Greate uno. –

+0

Poiché tutti i metodi su Enums.cs sono vincolati allo stesso modo, potresti non esprimerlo come un vincolo di classe (e puoi chiamarlo 'Enum '), o non funziona (o c'è qualche ragione per te? stai evitando quello? –

+0

@ Damen: Beh, i metodi di estensione non potrebbero essere in una classe generica. Potrei * potenzialmente avere una classe per i metodi di estensione e una classe per i metodi di non estensione ... ma non sono sicuro che farebbe molta differenza. –

1

A parte il fatto che i due diversi frammenti di codice non fanno la stessa cosa, mi piacerebbe usare questo:

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

Se non si dispone di .NET 4.0 Poi mi piacerebbe fare qualcosa di simile:

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

Questa è una Extension Method-string.

0

Trovo la variante dell'interruttore orribile dal momento che dovrai modificare l'interruttore ogni volta che cambi anche l'enumerazione.

Mi piace usare il TryParse che appartiene al tuo enum. Così si può usare in questo modo

string colorString = ..... 
colorEnum color; 

colorEnum.TryParse(colorString, out color); 

O se non vi interessa circa il caso della stringa

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

La returnValue TryParse è vero se la stringa è stato un enum valida, false se non.

2

Numero 1 semplicemente leggibilità e manutenibilità. Se si estende l'enumerazione, non è necessario eseguire ulteriori operazioni, mentre con 2 è necessario aggiungere più casi all'istruzione switch

0

Dal punto di vista delle prestazioni, poiché le enumerazioni sono implementate come campi statici, il metodo di analisi verrà probabilmente applicato finisce per fare refection su enum e poi prova un metodo GetField che potrebbe essere più veloce del caso. D'altra parte, se il 90% del caso, il colore è verde, il caso sarà molto veloce ... Si noti che il CLR a volte riorganizza il codice internamente, cambiando l'ordine del caso in base alla statistica (in realtà, io sono non è sicuro che lo faccia, ma il doc afferma che potrebbe farlo).

+3

al contrario, la riflessione è lenta quando melasses e switch sono fiammanti veloci - le stringhe su switch vengono implementate come un dizionario per convertire stringhe in interi e quindi un normale switch denso, che non ha nulla a che fare con le statistiche perché è un jump table non "IF lineare" che * non è usato comunque *, il compilatore crea un "albero di IF e alcuni interruttori per le parti dense" - che è ovviamente un dettaglio di implementazione – harold

+0

@harold: grazie – VdesmedT

2

Perché hai aggiunto il tag "prestazioni", ho intenzione di andare con l'interruttore.
Sì, sarà necessario modificare i casi quando si rinomina/aggiungi/rimuove qualsiasi elemento nell'enumerazione. Beh, è ​​solo un peccato allora. Qualsiasi variante di Enum.Parse/TryParse utilizza un sacco di codice strano e un po 'di riflessione, basta dare un'occhiata all'interno della funzione con ILSpy o simili. Poi c'è anche il problema di accettare "-12354" e anche un elenco separato da virgole di nomi validi (risultante in tutti loro ORed insieme) anche quando l'enum non ha un attributo [Flags].

In alternativa, è possibile creare un dizionario che traduca i nomi enum in valori. In realtà dovrebbe essere più veloce dello switch, perché anche un commutatore su stringhe passa attraverso un dizionario ma si salva la parte dell'interruttore effettiva.

Ovviamente entrambi i modi costano un po 'più di manutenzione rispetto enum.parse e varianti; se ne vale la pena è fino a tu, dal momento che di tutti noi solo tu hai abbastanza conoscenza del progetto per fare il trade delle prestazioni/coding-time.

0

Io uso il seguente, si ottiene tutto il tipo di sicurezza senza cadere ancora quando si aggiungono nuovi valori nell'Enum, è anche molto veloce.

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

Il mio test con 8 elementi della enum mostra in questo modo per essere più veloce rispetto al metodo interruttore.

Oppure si può usare (il modo molto lento):

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, mentre io sono totalmente bene con la soluzione Enum.Parse per gli scenari non-prestazioni (leggi: una tantum run di questa funzione occasionalmente ... e ci sono molti scenari di questo genere per essere sicuri), non riesco a tollerare il pensiero di possibilmente coinvolgere qualche metodo di tipo reflection quando questa funzione deve essere eseguita in un ciclo su centinaia/migliaia più i valori enum contemporaneamente.Gack!

Quindi la seguente è una soluzione che ottiene alcuni dei migliori di entrambi i mondi.

È sufficiente recuperare tutti i valori dell'enumerazione all'avvio o in caso contrario, ogni volta che funziona meglio (di seguito è riportato un modo per farlo) e quindi creare un dizionario con essi.

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