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

Se si esegue il codice sopra il risultato mostra:Perché Enum.Parse crea voci non definite?

Si dovrebbe mangiare almeno un 12345 al giorno.

Davvero mi aspettavo che una ArgumentException venisse lanciata se viene passato un nome sconosciuto (stringa). Prendendo un'occhiata da vicino la definizione Enum.Parse rivela:

Sommario:
converte la rappresentazione di stringa del nome o valore numerico di uno o più enumerati costanti per un oggetto equivalente elencate.

Eccezioni:
ArgumentException: enumType non è un Enum. -o-- il valore è una stringa vuota o contiene solo uno spazio bianco. - oppure - valore è un nome, ma non una delle costanti denominate definite per l'enumerazione.

I.e. se viene passata una rappresentazione di stringa di un intero, viene creato un nuovo valore enum e ora viene generata un'eccezione in base alla progettazione. Ha senso ciò?

Almeno ora so di chiamare Enum.IsDefined(enumType, value) prima Enum.Parse()

+1

Questa è una domanda? –

+0

Perché stai chiedendo e rispondendo alla tua stessa domanda? –

+0

La domanda viene posta sul comportamento ... – Markus

risposta

4

La "costante denominata" è la rappresentazione testuale del valore di un Enum, non il numero che gli è stato assegnato.

se si cambia:

string value = "12345"; 

A:

string value = "Cake"; 

Vedrete l'errore che stai aspettando, perché "il valore è un nome, ma non una delle costanti di nome definito per l'enumerazione. ". In questo caso il valore che stai passando in è un nome, "Torta", ma non uno nell'enumerazione.

Pensate Enum.Parse(enumType, value); nel seguente modo:

  1. Se value è un riferimento null, gettare un ArgumentNullException
  2. è il valore in value una delle costanti denominate nell'archivio di censimento nel enumType. Se sì, restituisci quel valore dall'enumerazione e fermati.
  3. È il valore in value direttamente convertibile nel tipo sottostante (in questo caso Int32), in caso affermativo, restituire tale valore e interrompere (anche se non esiste una costante denominata per tale valore).
  4. Il valore in value è convertibile direttamente nel tipo sottostante, ma al di fuori dell'intervallo del tipo sottostante? per esempio. il valore è una stringa contenente un numero uno maggiore di MAXINT. Se sì, lanciare un OverflowException.
  5. Il valore non è declassabile per il tipo sottostante? Se sì, lancia un ArgumentException.
+0

Hai ragione, dato che Enum è un Int32 Enum.Parse ("") è corretto nel restituire un valore valido. Il mio problema è stato causato vedendo Enum come Int32 costretto, che in realtà non lo è. – Markus

+0

Questo non è, in senso stretto, corretto. Una stringa non è mai "calcolabile" per un int, richiede una conversione possibile attraverso una varietà di metodi ('Convert.ToInt32',' int.Parse', 'int.TryParse', ecc.). Un'espressione più accurata di 3.) sarebbe "Il valore in' valore' direttamente * convertibile * nel tipo sottostante (in questo caso 'Int32')? Se sì, restituire quel valore e fermarsi (** anche se non c'è nominata costante per quel valore **). –

+0

@Adam, equo punto, ho cambiato la mia risposta di conseguenza =) – Rob

0

È necessario utilizzare 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

Ha già detto che nella sua domanda :) –

+0

Sì, lo so, ma non capisco perché Enum.Parse genera nuovi valori , è come chiamare Int.Parse ("grande numero sconosciuto all'umanità") e ottenere un risultato ... – Markus

0

io personalmente penso che sia un peccato che Enum.Parse accetta la stringa rappresentazione di un numero. Se stai cercando un'alternativa, potresti voler guardare il mio progetto Unconstrained Melody che ha varie opzioni di analisi ed è anche fortemente digitato.

È sicuramente possibile utilizzare Enum.IsDefined in concomitanza con l'analisi. Hai sicuramente vuoi accettare le versioni di stringa dei numeri? O stai solo aspettandoti dei nomi?

+1

È davvero un peccato che 'Enum.Parse' accetti la rappresentazione in stringa di un numero?Lo considererei rotto se non lo fosse, perché significherebbe che un valore enum potrebbe non essere il round trip verso e da una stringa, ad esempio "Enum.Parse (enumValue.ToString())" potrebbe non riuscire. –

3

Un enum può essere qualsiasi valore del suo tipo intero di base. Non è solo limitato a costanti nominate.

Ad esempio, la seguente è perfettamente valido:

enum Foo{ 
    A, 
    B, 
    C, 
    D 
} 

Foo x = (Foo)5; 

Anche se 5 non corrisponde a una costante denominata, è ancora un valore valido per Foo, poiché il tipo sottostante per Foo è Int32.

Se si dovesse chiamare x.ToString(), il valore restituito sarebbe semplicemente "5", poiché nessuna costante denominata corrisponde al valore di x.

Enum.Parse() è la funzione di conversione di Enum.ToString(). Dovresti aspettarti che qualunque cosa Enum.ToString() possa restituire che Enum.Parse() può accettare. Questo include, per esempio, valori separati da virgole per le bandiere enumerazioni:

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

uscita:

 
15 
A, B, C, D 
True 
True 

EDIT:

Cosa ti sembra veramente vogliamo è qualcosa di simile:

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 versione senza distinzione tra maiuscole e minuscole:

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

Grazie per l'approfondimento su 'Enum.Parse()' e 'ToString()', non sapevo sulla funzione con valori separati da virgola. – Markus

Problemi correlati