2010-03-31 35 views
8

devo enum come questo:Qual è il modo migliore per convertire enum in stringa?

public enum ObectTypes 
{ 
    TypeOne, 
    TypeTwo, 
    TypeThree, 
    ... 
    TypeTwenty 
} 

poi ho bisogno di convertire questo enum a stringa. Ora sto facendo così:

public string ConvertToCustomTypeName(ObjectTypes typeObj) 
{ 
    string result = string.Empty; 
    switch (typeObj) 
    { 
     case ObjectTypes.TypeOne: result = "This is type T123"; break; 
     case ObjectTypes.TypeTwo: result = "Oh man! This is type T234"; break; 
     ... 
     case ObjectTypes.TypeTwenty: result = "This is type last"; break; 
    } 

    return result; 
} 

Sono abbastanza sicuro che c'è modo migliore di fare questo, sto cercando qualche soluzione di buona pratica.

MODIFICA: non esiste un modello nella stringa di risultati.

Grazie in anticipo.

+0

Anche FWIW non è necessario l'oggetto risultato, basta solo restituire quelle stringhe ed essere in grado di eliminare anche le dichiarazioni di interruzione. –

+0

@Chris Marisic. Sì, hai ragione, ma quella variabile è solo per una lettura migliore :-) – Dariusz

risposta

19

ho utilizzare l'attributo [Description] da System.ComponentModel

Esempio:

public enum RoleType 
{ 
    [Description("Allows access to public information")] Guest = 0, 
    [Description("Allows access to the blog")] BlogReader = 4, 
} 

Quindi per leggere da esso faccio

public static string ReadDescription<T>(T enumMember) 
{ 
    var type = typeof (T); 

    var fi = type.GetField(enumMember.ToString()); 
    var attributes = (DescriptionAttribute[]) 
      fi.GetCustomAttributes(typeof (DescriptionAttribute), false); 
    return attributes.Length > 0 ? 
     attributes[0].Description : 
     enumMember.ToString(); 
} 

Poi utilizzo

ReadDescription(RoleType.Guest);

Nota: questa soluzione presuppone una singola applicazione cultura come nulla è stato espressamente chiesto di molteplici culture. Se ti trovi in ​​una situazione in cui devi gestire più culture, utilizzerei lo DescriptionAttribute o simile per memorizzare una chiave in un file di risorse compatibile con la cultura. Mentre è possibile memorizzare il membro enum direttamente nel file .resx che creerebbe l'accoppiamento più stretto possibile. Non vedo alcun motivo per cui vorresti accoppiare il funzionamento interno della tua applicazione (i nomi dei membri di enum) ai valori chiave che esistono ai fini dell'internazionalizzazione.

+3

Questo è tremendamente lento - può o non può importare, a seconda dell'utilizzo. –

+6

L'ottimizzazione prematura è il diavolo. Se la prestazione è una considerazione seria, è possibile implementare la memorizzazione nella cache e solo una volta è necessario leggere ogni membro di enum. Lo uso per popolare gli elenchi a discesa in modo da memorizzare nella cache le copie singleton delle mie enumerazioni Code/Value nel mio modello dopo aver ripetuto l'enumerazione una volta. Se fossi preoccupato del costo della riflessione, lo memorizzerei all'interno del metodo ReadDescription in qualche dizionario o hashtable, ecc. –

+0

Questa è una grande tecnica per gestire le enumerazioni, anche se sarebbe molto più pulita se l'enum stesso avesse un ' Metodo GetDescription' o alcuni di questi (e se è possibile inserire la descrizione * dopo * l'enumerazione). – MusiGenesis

2

Penso che il modo più semplice sia quello di avere una funzione che mappa tra il valore enum e il nome breve preferito e quindi crea un'altra funzione che genera il messaggio completo.

internal static string MapToName(ObjectTypes value) { 
    switch (value) { 
    case ObjectTypes.TypeOne: return "T123"; 
    case ObjectTypes.TypeTwo: return "T234"; 
    ... 
    } 
} 

public string ConvertToCustomTypeName(ObjectTypes value) { 
    return String.Format("This is type {0}", MapToName(value)); 
} 
+0

È ovvio che vuole convertirlo in una stringa arbitraria, non nel nome del campo enum. –

+0

@Matti, grazie mancato. Risposta aggiornata – JaredPar

+0

Il prefisso non è costante, il suffisso non è costante, quindi non posso prevedere l'inizio della stringa. – Dariusz

9

Se è necessaria una stringa personalizzata, l'opzione migliore sarebbe quella di creare un Dictionary< ObjectTypes, string> e fare semplicemente una ricerca nel dizionario.

Se stai bene con la funzionalità predefinita ToString(), basta usare typeObj.ToString();

Per l'approccio dizionario, si potrebbe fare:

private static Dictionary<ObjectTypes, string> enumLookup; 

static MyClass() 
{ 
    enumLookup = new Dictionary<ObjectTypes, string>(); 
    enumLookup.Add(ObjectTypes.TypeOne, "This is type T123"); 
    enumLookup.Add(ObjectTypes.TypeTwo, "This is type T234"); 
    // enumLookup.Add... 

} 

tuo metodo diventa:

public string ConvertToCustomTypeName(ObjectTypes typeObj) 
{ 
    // Shouldn't need TryGetValue, unless you're expecting people to mess with your enum values... 
    return enumLookup[typeObj]; 
} 
+0

Mi sento il bisogno di sottolineare che per le enumerazioni compatte (senza spazi vuoti di grande valore), una lista sarebbe molto più efficiente. –

+0

@Simon: Sì, potenzialmente - sebbene questo sia più flessibile, per qualsiasi tipo di valore nell'enumerazione, con qualsiasi valore enum arbitrario ... –

+0

@Simon: Anche se è vero anche se i valori enum iniziano o quasi 0 ... Si inizia a ottenere "valori" che non possono essere facilmente convertiti in indici di lista e l'hashing sarà più veloce. –

1

Una volta ho usato un attributo personalizzato nei campi enum che utilizzavano un parametro stringa e poi scriveva una funzione per estrarre la stringa quando veniva assegnato un valore enum.

+0

Huh, non sapevo che i membri di enum fossero attribuibili :). –

0

Questo richiede il refactoring, direi.

Replace Type Code With Class

+0

Non sono necessariamente d'accordo con questo, sarebbe un kill se fosse davvero un'enumerazione che ha valori di codice e valori di visualizzazione. Uso frequentemente Enums per popolare DropDowns. –

1

Se si desidera semplicemente utilizzare il nome di enum (cioè TypeOne) si può semplicemente chiamare ToString() sul enum sé

typeObj.ToString() 

Se si desidera una stringa personalizzata sulla base del scrivi che hai diverse opzioni. L'affermazione dell'interruttore che hai è OK, ma mantenerla diventerà caotica se hai un gran numero di enumerazioni. Oppure puoi impostare un dizionario basato sull'uso del tipo enum come chiave e sulla stringa come valore.

public enum ObectTypes 
{ 
    One, 
    Two 
} 

Dictionary<ObectTypes, String> myDic = new Dictionary<ObectTypes, string>(); 
myDic.Add(ObectTypes.One, "Something here for One"); 
myDic.Add(ObectTypes.Two, "Something here for Two"); 
1

Non riesco a credere questo ... perché nessuno ha suggerito un file di risorse?

La mappatura dei valori di enumerazione delle stringhe all'interno del codice compilato è buona e buona come un attacco rapido, ma a lungo termine è una cattiva pratica e rende difficile il refactoring del codice. Cosa succede se aggiungi (o sottrai) un valore dall'enumerazione? Se si usano stringhe da un file di risorse, tutto ciò che si deve fare è aggiungere (o rimuovere) una voce.

+1

Non penso che molti programmatori C# utilizzino il file di risorse. Sembra essere coder C++/win32/MFC che vedono la luce del file di risorse. – Justin

+0

@Justin - sono d'accordo, non ci sono abbastanza persone che li usano, forse una cattiva abitudine rimasta dai vecchi tempi di fare codice VB6, ecc. Penso anche che più persone diventano consapevoli di separare correttamente l'interfaccia utente dalla logica di business (cioè MVVM modello), quindi le persone inizieranno a utilizzare di più i file di risorse, soprattutto perché i file di risorse possono essere utilizzati come parte del collegamento statico. – slugster

+0

-1 Non sono d'accordo che sia una cattiva pratica, ho usato questo metodo per anni senza dolore. L'unica volta che non ha senso è se la lista deve cambiare dinamicamente e frequentemente, a quel punto le enumerazioni non hanno senso indipendentemente dall'uso delle descrizioni o meno. –

4

Utilizzare il modo in cui le risorse suggerito:

string GetName(Enum e) { 
    return Properties.Resources.ResourcesManager.GetString("_enum_"+e.GetType().ToString().Replace('.','_')); 
} 

La gestione degli errori è un po 'di più ..

1

vorrei mettere questi valori in un database. I ruoli di solito appartengono a un database per diversi motivi, tra cui:

  1. È possibile che i report vengano eseguiti in base ai ruoli, quindi è necessario visualizzare le descrizioni.
  2. Le definizioni ruolo possono essere configurate senza distribuire nuovo codice.
  3. I ruoli devono essere applicati con un vincolo di chiave.

Se eseguita correttamente, le prestazioni non dovrebbero rappresentare un problema per ottenere i ruoli dal database. Inoltre, poiché una tabella di definizione dei ruoli cambia raramente, è un ottimo candidato per la memorizzazione nella cache.

Se ti ritrovi a dover hackerare un Enum per farlo funzionare, forse non dovresti usare un Enum. Data la posizione corretta, tutto dovrebbe scorrere molto meglio nel codice con hack piccoli o nulli. Gli Enum garantiti hanno il loro posto; per esempio, uno stato piuttosto mutualmente esclusivo.

0

Quello che stavi già utilizzando non è un cattivo schema. Quasi ogni altra opzione ha i suoi problemi. Se si sta utilizzando F # per questo tipo, si potrebbe fare qualcosa di simile:

type ObjectTypes = 
| TypeOne 
| TypeTwo 
| TypeThree 
... 
| TypeTwenty 
with override this.ToString() = 
    match this with 
    | TypeOne -> "This is type T123" 
    | TypeTwo -> "Oh man! This is type T234" 
    ... 
    | TypeTwenty -> "This is type last" 
    | _ -> "This is any other type that wasn't explicitly specified" 

Naturalmente, con F # pattern matching, si ha un controllo molto più di quanto si farebbe con un semplice C# interruttore dichiarazione.

Problemi correlati