Rappresentazione di stringa di un Enum


912

Ho la seguente enumerazione:

public enum AuthenticationMethod
{
    FORMS = 1,
    WINDOWSAUTHENTICATION = 2,
    SINGLESIGNON = 3
}

Il problema però è che ho bisogno della parola "FORME" quando chiedo AuthenticationMethod.FORMS e non l'id 1.

Ho trovato la seguente soluzione per questo problema ( link ):

Per prima cosa devo creare un attributo personalizzato chiamato "StringValue":

public class StringValue : System.Attribute
{
    private readonly string _value;

    public StringValue(string value)
    {
        _value = value;
    }

    public string Value
    {
        get { return _value; }
    }

}

Quindi posso aggiungere questo attributo al mio enumeratore:

public enum AuthenticationMethod
{
    [StringValue("FORMS")]
    FORMS = 1,
    [StringValue("WINDOWS")]
    WINDOWSAUTHENTICATION = 2,
    [StringValue("SSO")]
    SINGLESIGNON = 3
}

E ovviamente ho bisogno di qualcosa per recuperare quel StringValue:

public static class StringEnum
{
    public static string GetStringValue(Enum value)
    {
        string output = null;
        Type type = value.GetType();

        //Check first in our cached results...

        //Look for our 'StringValueAttribute' 

        //in the field's custom attributes

        FieldInfo fi = type.GetField(value.ToString());
        StringValue[] attrs =
           fi.GetCustomAttributes(typeof(StringValue),
                                   false) as StringValue[];
        if (attrs.Length > 0)
        {
            output = attrs[0].Value;
        }

        return output;
    }
}

Bene, ora ho gli strumenti per ottenere un valore di stringa per un enumeratore. Posso quindi usarlo in questo modo:

string valueOfAuthenticationMethod = StringEnum.GetStringValue(AuthenticationMethod.FORMS);

Bene, ora funzionano tutti come un fascino, ma trovo molto lavoro. Mi chiedevo se ci fosse una soluzione migliore per questo.

Ho anche provato qualcosa con un dizionario e proprietà statiche, ma non era neanche meglio.


8
Bello! Posso usarlo per tradurre i valori di enum in stringhe localizzate.
Øyvind Skaar

5
Anche se potresti trovarlo così lungo, in realtà è un modo abbastanza flessibile di andare per altre cose. Come ha sottolineato uno dei miei colleghi, questo potrebbe essere usato in molti casi per sostituire gli Helpers Enum che mappano i codici del database per enumare i valori, ecc ...
BenAlabaster

27
MSDN consiglia le classi di attributi del suffisso con il suffisso "Attributo". Quindi "class StringValueAttribute";)
serhio

14
Sono d'accordo con @BenAlabaster che in realtà è abbastanza flessibile. Inoltre, potresti renderlo un metodo di estensione semplicemente aggiungendo thisdavanti al Enumtuo metodo statico. Quindi puoi farlo AuthenticationMethod.Forms.GetStringValue();
Justin Pihony,

5
Questo approccio utilizza la riflessione per leggere i valori degli attributi ed è molto lento se devi chiamare GetStringValue () molte volte nella mia esperienza. Il modello enumero sicuro è più veloce.
Rn222,

Risposte:


868

Prova il modello enumerazione sicura .

public sealed class AuthenticationMethod {

    private readonly String name;
    private readonly int value;

    public static readonly AuthenticationMethod FORMS = new AuthenticationMethod (1, "FORMS");
    public static readonly AuthenticationMethod WINDOWSAUTHENTICATION = new AuthenticationMethod (2, "WINDOWS");
    public static readonly AuthenticationMethod SINGLESIGNON = new AuthenticationMethod (3, "SSN");        

    private AuthenticationMethod(int value, String name){
        this.name = name;
        this.value = value;
    }

    public override String ToString(){
        return name;
    }

}

Aggiorna La conversione di tipo esplicita (o implicita) può essere eseguita da

  • aggiunta di campo statico con mappatura

    private static readonly Dictionary<string, AuthenticationMethod> instance = new Dictionary<string,AuthenticationMethod>();
    • nb Affinché l'inizializzazione dei campi "membro enum" non generi una NullReferenceException quando si chiama il costruttore dell'istanza, assicurarsi di inserire il campo Dizionario prima dei campi "membro enum" nella propria classe. Questo perché gli inizializzatori di campi statici vengono chiamati in ordine di dichiarazione e prima del costruttore statico, creando la situazione strana e necessaria ma confusa che il costruttore di istanze può essere chiamato prima che tutti i campi statici siano stati inizializzati e prima che venga chiamato il costruttore statico.
  • riempiendo questa mappatura nel costruttore dell'istanza

    instance[name] = this;
  • e aggiungendo un operatore di conversione del tipo definito dall'utente

    public static explicit operator AuthenticationMethod(string str)
    {
        AuthenticationMethod result;
        if (instance.TryGetValue(str, out result))
            return result;
        else
            throw new InvalidCastException();
    }

17
Sembra un enum ma non è un enum. Posso immaginare che causando alcuni problemi interessanti se le persone iniziano a provare a confrontare i metodi di autenticazione. Probabilmente dovrai sovraccaricare anche vari operatori di uguaglianza.
Formica,

36
@Ant: Non devo. Poiché abbiamo solo un'istanza di ogni AuthenticationMethod, l'uguaglianza di riferimento ereditata da Object funziona correttamente.
Jakub Šturc,

10
@tyriker: il compilatore lo fa. Il costruttore è privato, quindi non è possibile creare una nuova istanza. Anche i membri statici non sono accessibili tramite istanza.
Jakub Šturc,

21
@Jakub Molto interessante. Ho dovuto giocarci per capire come usarlo e realizzarne i benefici. È una classe pubblica, non statica, ma non può essere istanziata e puoi accedere solo ai suoi membri statici. Fondamentalmente, si comporta come un enum. Ma la parte migliore ... i membri statici sono digitati della classe e non una stringa generica o int. È un ... [aspetta] ... tipo enum sicuro! Grazie per avermi aiutato a capire.
Tyriker,

6
@kiran Di seguito ho pubblicato una versione leggermente modificata della risposta di Jakub Šturc che consente di utilizzarla con le dichiarazioni Switch-Case, quindi ora non vi è alcun
aspetto

228

Usa il metodo

Enum.GetName(Type MyEnumType,  object enumvariable)  

come in (Supponiamo che Shippersia un Enum definito)

Shipper x = Shipper.FederalExpress;
string s = Enum.GetName(typeof(Shipper), x);

Ci sono un sacco di altri metodi statici sulla classe Enum che vale la pena studiare anche ...


5
Esattamente. Ho creato un attributo personalizzato per una descrizione di stringa, ma è perché voglio una versione user-friendly (con spazi e altri caratteri speciali) che può essere facilmente associata a un ComboBox o simile.
lc.

5
Enum.GetName riflette i nomi dei campi nell'enum - lo stesso di .ToString (). Se le prestazioni sono un problema, possono essere un problema. Non mi preoccuperei se non stai convertendo un sacco di enumerazioni però.
Keith

8
Un'altra opzione da considerare, se hai bisogno di un enum con funzionalità extra, è "roll yr own" usando una struct ... aggiungi proprietà statiche di sola lettura per rappresentare i valori enum che sono inizializzati ai costruttori che generano singole istanze della struct ...
Charles Bretana,

1
quindi puoi aggiungere qualunque altro membro struct desideri, per implementare qualunque funzionalità desideri che questo "enum" abbia ...
Charles Bretana

2
Il problema qui è che GetName non è localizzabile. Non è sempre una preoccupazione, ma è qualcosa di cui essere consapevoli.
Joel Coehoorn,

79

È possibile fare riferimento al nome anziché al valore utilizzando ToString ()

Console.WriteLine("Auth method: {0}", AuthenticationMethod.Forms.ToString());

La documentazione è qui:

http://msdn.microsoft.com/en-us/library/16c1xs4z.aspx

... e se dai un nome ai tuoi enum in Pascal Case (come faccio io - come ThisIsMyEnumValue = 1 ecc.) allora potresti usare una regex molto semplice per stampare il modulo amichevole:

static string ToFriendlyCase(this string EnumString)
{
    return Regex.Replace(EnumString, "(?!^)([A-Z])", " $1");
}

che può essere facilmente chiamato da qualsiasi stringa:

Console.WriteLine("ConvertMyCrazyPascalCaseSentenceToFriendlyCase".ToFriendlyCase());

Uscite:

Converti la frase del mio pazzo Pascal in caso amichevole

Ciò consente di evitare di correre in giro per le case creando attributi personalizzati e collegandoli ai tuoi enum o usando le tabelle di ricerca per sposare un valore enum con una stringa amichevole e soprattutto autogestito e può essere utilizzato su qualsiasi stringa di Pascal Case che è infinitamente più riutilizzabile. Naturalmente, non ti consente di avere un nome descrittivo diverso dal tuo enum fornito dalla tua soluzione.

Mi piace la tua soluzione originale anche se per scenari più complessi. Potresti portare la tua soluzione un ulteriore passo avanti e rendere GetStringValue un metodo di estensione del tuo enum e quindi non dovrai fare riferimento a esso come StringEnum.GetStringValue ...

public static string GetStringValue(this AuthenticationMethod value)
{
  string output = null;
  Type type = value.GetType();
  FieldInfo fi = type.GetField(value.ToString());
  StringValue[] attrs = fi.GetCustomAttributes(typeof(StringValue), false) as StringValue[];
  if (attrs.Length > 0)
    output = attrs[0].Value;
  return output;
}

Potresti quindi accedervi facilmente direttamente dalla tua istanza enum:

Console.WriteLine(AuthenticationMethod.SSO.GetStringValue());

2
Ciò non aiuta se il "nome descrittivo" necessita di uno spazio. Come "Forms Authentication"
Ray Booysen,

4
Quindi assicurati che l'enum sia chiamato con maiuscole come FormsAuthentication e inserisci uno spazio prima di qualsiasi maiuscole che non sono all'inizio. Non è scienza missilistica inserire uno spazio in una stringa ...
BenAlabaster

4
La spaziatura automatica dei nomi dei casi Pascal diventa problematica se contengono abbreviazioni che devono essere in maiuscolo, ad esempio XML o GPS.
Richard Ev,

2
@RichardEv, non c'è regex perfetto per questo, ma eccone uno che dovrebbe funzionare un po 'meglio con le abbreviazioni. "(?!^)([^A-Z])([A-Z])", "$1 $2". Così HereIsATESTdiventa Here Is ATEST.
sparebytes

Non elegante fare questi piccoli "hack" che è quello che sono. Ho capito cosa sta dicendo l'OP e sto cercando di trovare una soluzione simile, ovvero usando l'eleganza di Enums ma potendo accedere facilmente al messaggio associato. L'unica soluzione a cui riesco a pensare è quella di applicare una sorta di mappatura tra il nome enum e un valore di stringa, ma ciò non risolve il problema del mantenimento dei dati di stringa (tuttavia lo rende pratico per gli scenari in cui è necessario disporre di più regioni, ecc. )
Tahir Khalid

72

Sfortunatamente la riflessione per ottenere gli attributi sugli enum è piuttosto lenta:

Vedi questa domanda: qualcuno conosce un modo rapido per ottenere attributi personalizzati su un valore enum?

L' .ToString()enumerazione è piuttosto lenta.

Puoi scrivere metodi di estensione per enum però:

public static string GetName( this MyEnum input ) {
    switch ( input ) {
        case MyEnum.WINDOWSAUTHENTICATION:
            return "Windows";
        //and so on
    }
}

Questo non è eccezionale, ma sarà rapido e non richiederà la riflessione per gli attributi o il nome del campo.


Aggiornamento C # 6

Se puoi usare C # 6, il nuovo nameofoperatore funziona per enum, quindi nameof(MyEnum.WINDOWSAUTHENTICATION)verrà convertito "WINDOWSAUTHENTICATION"in fase di compilazione , rendendolo il modo più veloce per ottenere i nomi enum.

Nota che questo convertirà l'enumerazione esplicita in una costante incorporata, quindi non funziona per gli enumerazioni che hai in una variabile. Così:

nameof(AuthenticationMethod.FORMS) == "FORMS"

Ma...

var myMethod = AuthenticationMethod.FORMS;
nameof(myMethod) == "myMethod"

24
È possibile recuperare i valori degli attributi una volta e inserirli in un dizionario <MyEnum, stringa> per mantenere l'aspetto dichiarativo.
Jon Skeet,

1
Sì, è quello che abbiamo finito per fare in un'app con un sacco di enumerazioni quando abbiamo scoperto che il riflesso era il collo di bottiglia.
Keith

Grazie Jon e Keith, ho finito per usare il tuo suggerimento sul dizionario. Funziona alla grande (e velocemente!).
Helge Klein,

@JonSkeet So che questo è vecchio. Ma come raggiungere questo obiettivo?
user919426,

2
@ user919426: raggiungere desideri? Metterli in un dizionario? Basta creare un dizionario, idealmente con un inizializzatore di raccolte ... non è chiaro cosa stai chiedendo.
Jon Skeet,

59

Uso un metodo di estensione:

public static class AttributesHelperExtension
    {
        public static string ToDescription(this Enum value)
        {
            var da = (DescriptionAttribute[])(value.GetType().GetField(value.ToString())).GetCustomAttributes(typeof(DescriptionAttribute), false);
            return da.Length > 0 ? da[0].Description : value.ToString();
        }
}

Ora decorare enumcon:

public enum AuthenticationMethod
{
    [Description("FORMS")]
    FORMS = 1,
    [Description("WINDOWSAUTHENTICATION")]
    WINDOWSAUTHENTICATION = 2,
    [Description("SINGLESIGNON ")]
    SINGLESIGNON = 3
}

Quando chiami

AuthenticationMethod.FORMS.ToDescription()otterrai "FORMS".


1
Ho dovuto aggiungere using System.ComponentModel;inoltre, questo metodo funziona solo se si desidera che il valore String sia lo stesso del nome dell'Enum. OP voleva un valore diverso.
elcool,

2
Non intendi quando chiami AuthenticationMethod.FORMS.ToDescription()?
nicodemus13,

41

Usa il ToString()metodo

public enum any{Tomato=0,Melon,Watermelon}

Per fare riferimento alla stringa Tomato, basta usare

any.Tomato.ToString();

Wow. È stato facile. So che l'OP voleva aggiungere descrizioni di stringhe personalizzate, ma questo è ciò di cui avevo bisogno. Avrei dovuto saperlo, in retrospettiva, ma ho seguito il percorso Enum.GetName.
Rafe,

7
Perché tutti gli altri lo complicano troppo?
Brent,

18
@Brent Perché molto spesso hai un .ToString()valore diverso da quello user friendly di cui hai bisogno.
Novitchi S,

2
@Brent - perché questo è diverso dalla domanda che viene posta. La domanda che si pone è come ottenere questa stringa da una variabile a cui è stato assegnato un valore enumerato. Questo è dinamico in fase di esecuzione. Questo sta controllando la definizione del tipo e impostato in fase di esecuzione.
Hogan,

1
@Hogan - ToString () funziona anche su variabili: any fruit = any.Tomato; string tomato = fruit.ToString();
LiborV

29

Soluzione molto semplice con .Net 4.0 e versioni successive. Non è necessario altro codice.

public enum MyStatus
{
    Active = 1,
    Archived = 2
}

Per ottenere la stringa basta usare:

MyStatus.Active.ToString("f");

o

MyStatus.Archived.ToString("f");`

Il valore sarà "Attivo" o "Archiviato".

Per vedere i diversi formati di stringa (la "f" dall'alto) durante la chiamata, Enum.ToStringvedere questa pagina Stringhe del formato di enumerazione


28

Uso l'attributo Descrizione dallo spazio dei nomi System.ComponentModel. Decorare semplicemente l'enum e quindi utilizzare questo codice per recuperarlo:

public static string GetDescription<T>(this object enumerationValue)
            where T : struct
        {
            Type type = enumerationValue.GetType();
            if (!type.IsEnum)
            {
                throw new ArgumentException("EnumerationValue must be of Enum type", "enumerationValue");
            }

            //Tries to find a DescriptionAttribute for a potential friendly name
            //for the enum
            MemberInfo[] memberInfo = type.GetMember(enumerationValue.ToString());
            if (memberInfo != null && memberInfo.Length > 0)
            {
                object[] attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);

                if (attrs != null && attrs.Length > 0)
                {
                    //Pull out the description value
                    return ((DescriptionAttribute)attrs[0]).Description;
                }
            }
            //If we have no description attribute, just return the ToString of the enum
            return enumerationValue.ToString();

        }

Come esempio:

public enum Cycle : int
{        
   [Description("Daily Cycle")]
   Daily = 1,
   Weekly,
   Monthly
}

Questo codice si adatta perfettamente agli enum in cui non è necessario un "nome descrittivo" e restituirà solo il .ToString () dell'enum.


27

Mi piace molto la risposta di Jakub Šturc, ma il suo difetto è che non puoi usarla con un'istruzione switch-case. Ecco una versione leggermente modificata della sua risposta che può essere utilizzata con un'istruzione switch:

public sealed class AuthenticationMethod
{
    #region This code never needs to change.
    private readonly string _name;
    public readonly Values Value;

    private AuthenticationMethod(Values value, String name){
        this._name = name;
        this.Value = value;
    }

    public override String ToString(){
        return _name;
    }
    #endregion

    public enum Values
    {
        Forms = 1,
        Windows = 2,
        SSN = 3
    }

    public static readonly AuthenticationMethod FORMS = new AuthenticationMethod (Values.Forms, "FORMS");
    public static readonly AuthenticationMethod WINDOWSAUTHENTICATION = new AuthenticationMethod (Values.Windows, "WINDOWS");
    public static readonly AuthenticationMethod SINGLESIGNON = new AuthenticationMethod (Values.SSN, "SSN");
}

Quindi ottieni tutti i vantaggi della risposta di Jakub Šturc, inoltre possiamo usarlo con un'istruzione switch in questo modo:

var authenticationMethodVariable = AuthenticationMethod.FORMS;  // Set the "enum" value we want to use.
var methodName = authenticationMethodVariable.ToString();       // Get the user-friendly "name" of the "enum" value.

// Perform logic based on which "enum" value was chosen.
switch (authenticationMethodVariable.Value)
{
    case authenticationMethodVariable.Values.Forms: // Do something
        break;
    case authenticationMethodVariable.Values.Windows: // Do something
        break;
    case authenticationMethodVariable.Values.SSN: // Do something
        break;      
}

Una soluzione più breve sarebbe quella di rimuovere gli enumerati {} e tenere invece un conteggio statico di quanti Enum hai costruito. Ciò offre anche il vantaggio di non dover aggiungere una nuova istanza apportata all'elenco enum. es. public static int nextAvailable { get; private set; }quindi nel costruttorethis.Value = nextAvailable++;
kjhf

Idea interessante @kjhf. La mia preoccupazione sarebbe che se qualcuno riordina il codice, anche il valore assegnato ai valori enum potrebbe cambiare. Ad esempio, ciò potrebbe comportare il recupero del valore enum errato quando il valore enum viene salvato in un file / database, l'ordine delle righe "new AuthenticationMethod (...)" viene modificato (ad esempio, uno viene rimosso) e quindi eseguire nuovamente l'app e recuperare il valore enum dal file / database; il valore enum potrebbe non corrispondere al metodo Authentication che era stato originariamente salvato.
cane mortale,

Un buon punto - anche se spero in questi casi particolari la gente non si affiderà al valore intero dell'enum (o al riordino del codice enum.) - e questo valore è usato puramente come un interruttore e forse un'alternativa a .Equals () e. GetHashCode (). Se preoccupato, potresti sempre fare un grande commento con "NON RIORDINARE": p
kjhf,

Non puoi semplicemente sovraccaricare l' =operatore per consentire il passaggio al funzionamento? L'ho fatto in VB e ora posso usarlo in select casedichiarazione.
user1318499

@ user1318499 No, C # ha regole più rigide attorno all'istruzione switch rispetto a VB. Non è possibile utilizzare le istanze di classe per l'istruzione Case; puoi usare solo primitivi costanti.
cane mortale

13

Uso una combinazione di alcuni dei suggerimenti sopra, combinati con un po 'di cache. Ora, ho avuto l'idea da un codice che ho trovato da qualche parte sulla rete, ma non ricordo né dove l'ho trovato né lo trovo. Quindi, se qualcuno trova qualcosa di simile, si prega di commentare con l'attribuzione.

Ad ogni modo, l'utilizzo coinvolge i convertitori di tipi, quindi se si esegue il binding all'interfaccia utente, "funziona". Puoi estenderlo con il modello di Jakub per una rapida ricerca del codice inizializzando dal convertitore di tipi nei metodi statici.

L'utilizzo di base sarebbe simile a questo

[TypeConverter(typeof(CustomEnumTypeConverter<MyEnum>))]
public enum MyEnum
{
    // The custom type converter will use the description attribute
    [Description("A custom description")]
    ValueWithCustomDescription,

   // This will be exposed exactly.
   Exact
}

Di seguito è riportato il codice per il convertitore di tipi di enum personalizzato:

public class CustomEnumTypeConverter<T> : EnumConverter
    where T : struct
{
    private static readonly Dictionary<T,string> s_toString = 
      new Dictionary<T, string>();

    private static readonly Dictionary<string, T> s_toValue = 
      new Dictionary<string, T>();

    private static bool s_isInitialized;

    static CustomEnumTypeConverter()
    {
        System.Diagnostics.Debug.Assert(typeof(T).IsEnum,
          "The custom enum class must be used with an enum type.");
    }

    public CustomEnumTypeConverter() : base(typeof(T))
    {
        if (!s_isInitialized)
        {
            Initialize();
            s_isInitialized = true;
        }
    }

    protected void Initialize()
    {
        foreach (T item in Enum.GetValues(typeof(T)))
        {
            string description = GetDescription(item);
            s_toString[item] = description;
            s_toValue[description] = item;
        }
    }

    private static string GetDescription(T optionValue)
    {
        var optionDescription = optionValue.ToString();
        var optionInfo = typeof(T).GetField(optionDescription);
        if (Attribute.IsDefined(optionInfo, typeof(DescriptionAttribute)))
        {
            var attribute = 
              (DescriptionAttribute)Attribute.
                 GetCustomAttribute(optionInfo, typeof(DescriptionAttribute));
            return attribute.Description;
        }
        return optionDescription;
    }

    public override object ConvertTo(ITypeDescriptorContext context, 
       System.Globalization.CultureInfo culture, 
       object value, Type destinationType)
    {
        var optionValue = (T)value;

        if (destinationType == typeof(string) && 
            s_toString.ContainsKey(optionValue))
        {
            return s_toString[optionValue];
        }

        return base.ConvertTo(context, culture, value, destinationType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, 
       System.Globalization.CultureInfo culture, object value)
    {
        var stringValue = value as string;

        if (!string.IsNullOrEmpty(stringValue) && s_toValue.ContainsKey(stringValue))
        {
            return s_toValue[stringValue];
        }

        return base.ConvertFrom(context, culture, value);
    }
}

}


12

Nella tua domanda non hai mai detto che in realtà hai bisogno del valore numerico dell'enum ovunque.

Se non hai e hai solo bisogno di un enum di tipo stringa (che non è un tipo integrale, quindi non può essere una base di enum) ecco un modo:

    static class AuthenticationMethod
    {
        public static readonly string
            FORMS = "Forms",
            WINDOWSAUTHENTICATION = "WindowsAuthentication";
    }

puoi usare la stessa sintassi di enum per fare riferimento a essa

if (bla == AuthenticationMethod.FORMS)

Sarà un po 'più lento rispetto ai valori numerici (confrontando le stringhe anziché i numeri) ma sul lato positivo non sta usando la riflessione (lenta) per accedere alla stringa.


se si utilizza "const" anziché "statico in sola lettura", è possibile utilizzare i valori come etichette maiuscole in un'istruzione switch.
Ed N.

11

Come ho risolto questo come metodo di estensione:

using System.ComponentModel;
public static string GetDescription(this Enum value)
{
    var descriptionAttribute = (DescriptionAttribute)value.GetType()
        .GetField(value.ToString())
        .GetCustomAttributes(false)
        .Where(a => a is DescriptionAttribute)
        .FirstOrDefault();

    return descriptionAttribute != null ? descriptionAttribute.Description : value.ToString();
}

enum:

public enum OrderType
{
    None = 0,
    [Description("New Card")]
    NewCard = 1,
    [Description("Reload")]
    Refill = 2
}

Utilizzo (dove o.OrderType è una proprietà con lo stesso nome dell'enum):

o.OrderType.GetDescription()

Il che mi dà una stringa di "Nuova carta" o "Ricarica" ​​invece del valore enum effettivo NewCard e Refill.


Per completezza, è necessario includere una copia della classe DescriptionAttribute.
Bernie White

3
Bernie, Description L'
attributo

11

Aggiornamento: visitando questa pagina, 8 anni dopo, dopo aver toccato C # per molto tempo, sembra che la mia risposta non sia più la soluzione migliore. Mi piace molto la soluzione di conversione legata alle funzioni degli attributi.

Se stai leggendo questo, assicurati di controllare anche altre risposte.
(suggerimento: sono sopra questo)


Come molti di voi, mi è piaciuta molto la risposta selezionata di Jakub Šturc , ma odio anche copiare e incollare il codice e provare a farlo il meno possibile.

Così ho deciso che volevo una classe EnumBase da cui la maggior parte delle funzionalità è ereditata / integrata, lasciandomi concentrarmi sul contenuto anziché sul comportamento.

Il problema principale con questo approccio si basa sul fatto che sebbene i valori di Enum siano istanze di tipo sicuro, l'interazione è con l'implementazione statica del tipo di classe Enum. Quindi, con un piccolo aiuto di magia generica, penso di aver finalmente ottenuto il mix giusto. Spero che qualcuno lo trovi utile quanto me.

Inizierò con l'esempio di Jakub, ma usando l'ereditarietà e i generici:

public sealed class AuthenticationMethod : EnumBase<AuthenticationMethod, int>
{
    public static readonly AuthenticationMethod FORMS =
        new AuthenticationMethod(1, "FORMS");
    public static readonly AuthenticationMethod WINDOWSAUTHENTICATION =
        new AuthenticationMethod(2, "WINDOWS");
    public static readonly AuthenticationMethod SINGLESIGNON =
        new AuthenticationMethod(3, "SSN");

    private AuthenticationMethod(int Value, String Name)
        : base( Value, Name ) { }
    public new static IEnumerable<AuthenticationMethod> All
    { get { return EnumBase<AuthenticationMethod, int>.All; } }
    public static explicit operator AuthenticationMethod(string str)
    { return Parse(str); }
}

Ed ecco la classe base:

using System;
using System.Collections.Generic;
using System.Linq; // for the .AsEnumerable() method call

// E is the derived type-safe-enum class
// - this allows all static members to be truly unique to the specific
//   derived class
public class EnumBase<E, T> where E: EnumBase<E, T>
{
    #region Instance code
    public T Value { get; private set; }
    public string Name { get; private set; }

    protected EnumBase(T EnumValue, string Name)
    {
        Value = EnumValue;
        this.Name = Name;
        mapping.Add(Name, this);
    }

    public override string ToString() { return Name; }
    #endregion

    #region Static tools
    static private readonly Dictionary<string, EnumBase<E, T>> mapping;
    static EnumBase() { mapping = new Dictionary<string, EnumBase<E, T>>(); }
    protected static E Parse(string name)
    {
        EnumBase<E, T> result;
        if (mapping.TryGetValue(name, out result))
        {
            return (E)result;
        }

        throw new InvalidCastException();
    }
    // This is protected to force the child class to expose it's own static
    // method.
    // By recreating this static method at the derived class, static
    // initialization will be explicit, promising the mapping dictionary
    // will never be empty when this method is called.
    protected static IEnumerable<E> All
    { get { return mapping.Values.AsEnumerable().Cast<E>(); } }
    #endregion
}

Potrebbe essere possibile chiamare il costruttore statico derivato dal costruttore statico di base. Ci sto ancora esaminando, ma finora non ho riscontrato alcun problema: stackoverflow.com/questions/55290034/…
Cory-G

10

Sono d'accordo con Keith, ma non posso votare (ancora).

Uso un metodo statico e un'istruzione swith per restituire esattamente quello che voglio. Nel database memorizzo tinyint e il mio codice utilizza solo l'enum reale, quindi le stringhe sono per i requisiti dell'interfaccia utente. Dopo numerosi test ciò ha prodotto le migliori prestazioni e il massimo controllo sull'output.

public static string ToSimpleString(this enum)
{
     switch (enum)
     {
         case ComplexForms:
             return "ComplexForms";
             break;
     }
}

public static string ToFormattedString(this enum)
{
     switch (enum)
     {
         case ComplexForms:
             return "Complex Forms";
             break;
     }
}

Tuttavia, secondo alcuni account, ciò porta a un possibile incubo di manutenzione e un po 'di odore di codice. Cerco di tenere d'occhio gli enum che sono lunghi e molti enum o quelli che cambiano frequentemente. Altrimenti, questa è stata un'ottima soluzione per me.


10

Se sei venuto qui cercando di implementare un semplice "Enum" ma i cui valori sono stringhe anziché ints, ecco la soluzione più semplice:

    public sealed class MetricValueList
    {
        public static readonly string Brand = "A4082457-D467-E111-98DC-0026B9010912";
        public static readonly string Name = "B5B5E167-D467-E111-98DC-0026B9010912";
    }

Implementazione:

var someStringVariable = MetricValueList.Brand;

2
Probabilmente è meglio rendere le variabili costi invece di usare static readonly.
AndyGeek,

1
i costi non sono buoni per le classi accessibili al pubblico, poiché vengono elaborati in fase di compilazione, non è possibile sostituire una DLL di terze parti senza ricompilare l'intero codice con i costi.
Kristian Williams,

7

Quando mi trovo di fronte a questo problema, ci sono un paio di domande a cui provo a trovare prima le risposte:

  • I nomi dei miei valori enum sono sufficientemente amichevoli allo scopo o devo fornire quelli più amichevoli?
  • Ho bisogno di andata e ritorno? Cioè, dovrò prendere valori di testo e analizzarli in valori enum?
  • È qualcosa che devo fare per molte enumerazioni nel mio progetto o solo una?
  • Che tipo di elementi dell'interfaccia utente presenterò queste informazioni, in particolare, vincolerò l'interfaccia utente o utilizzerò le finestre delle proprietà?
  • Questo deve essere localizzabile?

Il modo più semplice per farlo è con Enum.GetValue(e supporta il round-trip utilizzando Enum.Parse). Spesso vale anche la pena costruire un TypeConverter, come suggerisce Steve Mitcham, per supportare il binding dell'interfaccia utente. (Non è necessario crearne uno TypeConverterquando si utilizzano le schede delle proprietà, che è una delle cose belle delle schede delle proprietà. Anche se Lord sa che hanno i loro problemi.)

In generale, se le risposte alle domande precedenti suggeriscono che non funzionerà, il mio prossimo passo è creare e popolare uno statico Dictionary<MyEnum, string>, o possibilmente un Dictionary<Type, Dictionary<int, string>>. Tendo a saltare il passaggio intermedio decorare-il-codice-con-attributi perché ciò che di solito viene dopo il luccio è la necessità di cambiare i valori amichevoli dopo la distribuzione (spesso, ma non sempre, a causa della localizzazione).


7

Volevo pubblicare questo come commento al post citato di seguito, ma non potevo perché non ho abbastanza rappresentante, quindi per favore non votare in negativo. Il codice conteneva un errore e volevo segnalarlo alle persone che cercavano di utilizzare questa soluzione:

[TypeConverter(typeof(CustomEnumTypeConverter(typeof(MyEnum))]
public enum MyEnum
{
  // The custom type converter will use the description attribute
  [Description("A custom description")]
  ValueWithCustomDescription,
  // This will be exposed exactly.
  Exact
}

dovrebbe essere

[TypeConverter(typeof(CustomEnumTypeConverter<MyEnum>))]
public enum MyEnum
{
  // The custom type converter will use the description attribute
  [Description("A custom description")]
  ValueWithCustomDescription,

  // This will be exposed exactly.
  Exact
}

Brillant!


5

La mia variante

public struct Colors
{
    private String current;

    private static string red = "#ff0000";
    private static string green = "#00ff00";
    private static string blue = "#0000ff";

    private static IList<String> possibleColors; 

    public static Colors Red { get { return (Colors) red; } }
    public static Colors Green { get { return (Colors) green; } }
    public static Colors Blue { get { return (Colors) blue; } }

    static Colors()
    {
        possibleColors = new List<string>() {red, green, blue};
    }

    public static explicit operator String(Colors value)
    {
        return value.current;
    }

    public static explicit operator Colors(String value)
    {
        if (!possibleColors.Contains(value))
        {
            throw new InvalidCastException();
        }

        Colors color = new Colors();
        color.current = value;
        return color;
    }

    public static bool operator ==(Colors left, Colors right)
    {
        return left.current == right.current;
    }

    public static bool operator !=(Colors left, Colors right)
    {
        return left.current != right.current;
    }

    public bool Equals(Colors other)
    {
        return Equals(other.current, current);
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (obj.GetType() != typeof(Colors)) return false;
        return Equals((Colors)obj);
    }

    public override int GetHashCode()
    {
        return (current != null ? current.GetHashCode() : 0);
    }

    public override string ToString()
    {
        return current;
    }
}

Il codice sembra un po 'brutto, ma gli usi di questa struttura sono piuttosto presentativi.

Colors color1 = Colors.Red;
Console.WriteLine(color1); // #ff0000

Colors color2 = (Colors) "#00ff00";
Console.WriteLine(color2); // #00ff00

// Colors color3 = "#0000ff"; // Compilation error
// String color4 = Colors.Red; // Compilation error

Colors color5 = (Colors)"#ff0000";
Console.WriteLine(color1 == color5); // True

Colors color6 = (Colors)"#00ff00";
Console.WriteLine(color1 == color6); // False

Inoltre, penso che, se fosse richiesto un sacco di enumerazioni del genere, potrebbe essere utilizzata la generazione di codice (ad es. T4).


4

Opzione 1:

public sealed class FormsAuth
{
     public override string ToString{return "Forms Authtentication";}
}
public sealed class WindowsAuth
{
     public override string ToString{return "Windows Authtentication";}
}

public sealed class SsoAuth
{
     public override string ToString{return "SSO";}
}

e poi

object auth = new SsoAuth(); //or whatever

//...
//...
// blablabla

DoSomethingWithTheAuth(auth.ToString());

Opzione 2:

public enum AuthenticationMethod
{
        FORMS = 1,
        WINDOWSAUTHENTICATION = 2,
        SINGLESIGNON = 3
}

public class MyClass
{
    private Dictionary<AuthenticationMethod, String> map = new Dictionary<AuthenticationMethod, String>();
    public MyClass()
    {
         map.Add(AuthenticationMethod.FORMS,"Forms Authentication");
         map.Add(AuthenticationMethod.WINDOWSAUTHENTICATION ,"Windows Authentication");
         map.Add(AuthenticationMethod.SINGLESIGNON ,"SSo Authentication");
    }
}

4

Se pensi al problema che stiamo cercando di risolvere, non è affatto un enum di cui abbiamo bisogno. Abbiamo bisogno di un oggetto che consenta di associare un determinato numero di valori; in altre parole, per definire una classe.

Il modello enum sicuro di Jakub Šturc è l'opzione migliore che vedo qui.

Guardarlo:

  • Ha un costruttore privato, quindi solo la classe stessa può definire i valori consentiti.
  • È una classe chiusa quindi i valori non possono essere modificati tramite l'ereditarietà.
  • È sicuro per i tipi, consentendo ai tuoi metodi di richiedere solo quel tipo.
  • Non vi è alcun impatto sulle prestazioni di riflessione dovuto all'accesso ai valori.
  • Infine, può essere modificato per associare più di due campi insieme, ad esempio un nome, una descrizione e un valore numerico.

4

per me, l'approccio pragmatico è classe dentro classe, esempio:

public class MSEModel
{
    class WITS
    {
        public const string DATE = "5005";
        public const string TIME = "5006";
        public const string MD = "5008";
        public const string ROP = "5075";
        public const string WOB = "5073";
        public const string RPM = "7001";
... 
    }

4

Ho creato una classe di base per la creazione di enumerazioni con valore di stringa in .NET. È solo un file C # che puoi copiare e incollare nei tuoi progetti o installare tramite il pacchetto NuGet chiamato StringEnum . GitHub Repo

  • Intellisense suggerirà il nome enum se la classe è annotata con il commento xml <completitionlist>. (Funziona in C # e VB)

Demo Intellisense

  • Uso simile a un enum regolare:
///<completionlist cref="HexColor"/> 
class HexColor : StringEnum<HexColor>
{
    public static readonly HexColor Blue = Create("#FF0000");
    public static readonly HexColor Green = Create("#00FF00");
    public static readonly HexColor Red = Create("#000FF");
}
    // Static Parse Method
    HexColor.Parse("#FF0000") // => HexColor.Red
    HexColor.Parse("#ff0000", caseSensitive: false) // => HexColor.Red
    HexColor.Parse("invalid") // => throws InvalidOperationException

    // Static TryParse method.
    HexColor.TryParse("#FF0000") // => HexColor.Red
    HexColor.TryParse("#ff0000", caseSensitive: false) // => HexColor.Red
    HexColor.TryParse("invalid") // => null

    // Parse and TryParse returns the preexistent instances
    object.ReferenceEquals(HexColor.Parse("#FF0000"), HexColor.Red) // => true

    // Conversion from your `StringEnum` to `string`
    string myString1 = HexColor.Red.ToString(); // => "#FF0000"
    string myString2 = HexColor.Red; // => "#FF0000" (implicit cast)

instalation:

  • Incolla la seguente classe base StringEnum nel tuo progetto. ( ultima versione )
  • Oppure installa il pacchetto StringEnum NuGet, che si basa su in .Net Standard 1.0modo che venga eseguito su .Net Core> = 1.0, .Net Framework> = 4.5, Mono> = 4.6, ecc.
    /// <summary>
    /// Base class for creating string-valued enums in .NET.<br/>
    /// Provides static Parse() and TryParse() methods and implicit cast to string.
    /// </summary>
    /// <example> 
    /// <code>
    /// class Color : StringEnum &lt;Color&gt;
    /// {
    ///     public static readonly Color Blue = Create("Blue");
    ///     public static readonly Color Red = Create("Red");
    ///     public static readonly Color Green = Create("Green");
    /// }
    /// </code>
    /// </example>
    /// <typeparam name="T">The string-valued enum type. (i.e. class Color : StringEnum&lt;Color&gt;)</typeparam>
    public abstract class StringEnum<T> : IEquatable<T> where T : StringEnum<T>, new()
    {
        protected string Value;
        private static Dictionary<string, T> valueDict = new Dictionary<string, T>();
        protected static T Create(string value)
        {
            if (value == null)
                return null; // the null-valued instance is null.

            var result = new T() { Value = value };
            valueDict.Add(value, result);
            return result;
        }

        public static implicit operator string(StringEnum<T> enumValue) => enumValue.Value;
        public override string ToString() => Value;

        public static bool operator !=(StringEnum<T> o1, StringEnum<T> o2) => o1?.Value != o2?.Value;
        public static bool operator ==(StringEnum<T> o1, StringEnum<T> o2) => o1?.Value == o2?.Value;

        public override bool Equals(object other) => this.Value.Equals((other as T)?.Value ?? (other as string));
        bool IEquatable<T>.Equals(T other) => this.Value.Equals(other.Value);
        public override int GetHashCode() => Value.GetHashCode();

        /// <summary>
        /// Parse the <paramref name="value"/> specified and returns a valid <typeparamref name="T"/> or else throws InvalidOperationException.
        /// </summary>
        /// <param name="value">The string value representad by an instance of <typeparamref name="T"/>. Matches by string value, not by the member name.</param>
        /// <param name="caseSensitive">If true, the strings must match case and takes O(log n). False allows different case but is little bit slower (O(n))</param>
        public static T Parse(string value, bool caseSensitive = true)
        {
            var result = TryParse(value, caseSensitive);
            if (result == null)
                throw new InvalidOperationException((value == null ? "null" : $"'{value}'") + $" is not a valid {typeof(T).Name}");

            return result;
        }

        /// <summary>
        /// Parse the <paramref name="value"/> specified and returns a valid <typeparamref name="T"/> or else returns null.
        /// </summary>
        /// <param name="value">The string value representad by an instance of <typeparamref name="T"/>. Matches by string value, not by the member name.</param>
        /// <param name="caseSensitive">If true, the strings must match case. False allows different case but is slower: O(n)</param>
        public static T TryParse(string value, bool caseSensitive = true)
        {
            if (value == null) return null;
            if (valueDict.Count == 0) System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle); // force static fields initialization
            if (caseSensitive)
            {
                if (valueDict.TryGetValue(value, out T item))
                    return item;
                else
                    return null;
            }
            else
            {
                // slower O(n) case insensitive search
                return valueDict.FirstOrDefault(f => f.Key.Equals(value, StringComparison.OrdinalIgnoreCase)).Value;
                // Why Ordinal? => https://esmithy.net/2007/10/15/why-stringcomparisonordinal-is-usually-the-right-choice/
            }
        }
    }

3

Ecco ancora un altro modo per eseguire il compito di associare le stringhe agli enum:

struct DATABASE {
    public enum enums {NOTCONNECTED, CONNECTED, ERROR}
    static List<string> strings =
        new List<string>() {"Not Connected", "Connected", "Error"};

    public string GetString(DATABASE.enums value) {
        return strings[(int)value];
    }
}

Questo metodo si chiama così:

public FormMain() {
    DATABASE dbEnum;

    string enumName = dbEnum.GetString(DATABASE.enums.NOTCONNECTED);
}

Puoi raggruppare enumerazioni correlate nella loro struttura. Poiché questo metodo utilizza il tipo enum, è possibile utilizzare Intellisense per visualizzare l'elenco di enum quando si effettua il fileGetString() chiamata.

Se lo si desidera, è possibile utilizzare il nuovo operatore sulla DATABASEstruttura. Non utilizzarlo significa che le stringhe Listnon vengono allocate fino alla prima GetString()chiamata.


3

Molte grandi risposte qui, ma nel mio caso non hanno risolto ciò che volevo da un "enum stringa", che era:

  1. Utilizzabile in un'istruzione switch, ad esempio switch (myEnum)
  2. Può essere utilizzato nei parametri delle funzioni, ad es. Foo (tipo myEnum)
  3. Può essere referenziato ad esempio myEnum.FirstElement
  4. Posso usare stringhe, ad esempio foo ("FirstElement") == foo (myEnum.FirstElement)

1,2 e 4 possono effettivamente essere risolti con un typedef C # di una stringa (poiché le stringhe sono commutabili in c #)

3 può essere risolto con stringhe const statiche. Quindi, se hai le stesse esigenze, questo è l'approccio più semplice:

public sealed class Types
{

    private readonly String name;

    private Types(String name)
    {
        this.name = name;

    }

    public override String ToString()
    {
        return name;
    }

    public static implicit operator Types(string str)
    {
        return new Types(str);

    }
    public static implicit operator string(Types str)
    {
        return str.ToString();
    }


    #region enum

    public const string DataType = "Data";
    public const string ImageType = "Image";
    public const string Folder = "Folder";
    #endregion

}

Ciò consente ad esempio:

    public TypeArgs(Types SelectedType)
    {
        Types SelectedType = SelectedType
    }

e

public TypeObject CreateType(Types type)
    {
        switch (type)
        {

            case Types.ImageType:
              //
                break;

            case Types.DataType:
             //
                break;

        }
    }

Dove CreateType può essere chiamato con una stringa o un tipo. Tuttavia, il rovescio della medaglia è che qualsiasi stringa è automaticamente un enum valido , questo potrebbe essere modificato ma quindi richiederebbe un qualche tipo di funzione init ... o forse renderebbe esplicito il cast interno?

Ora, se un valore int è importante per voi (forse per la velocità di confronto), si potrebbe utilizzare alcune idee da Jakub Šturc risposta fantastica e fare qualcosa di un po ' folle, questo è il mio pugnalata a lui:

    public sealed class Types
{
    private static readonly Dictionary<string, Types> strInstance = new Dictionary<string, Types>();
    private static readonly Dictionary<int, Types> intInstance = new Dictionary<int, Types>();

    private readonly String name;
    private static int layerTypeCount = 0;
    private int value;
    private Types(String name)
    {
        this.name = name;
        value = layerTypeCount++;
        strInstance[name] = this;
        intInstance[value] = this;
    }

    public override String ToString()
    {
        return name;
    }


    public static implicit operator Types(int val)
    {
        Types result;
        if (intInstance.TryGetValue(val, out result))
            return result;
        else
            throw new InvalidCastException();
    }

    public static implicit operator Types(string str)
    {
        Types result;
        if (strInstance.TryGetValue(str, out result))
        {
            return result;
        }
        else
        {
            result = new Types(str);
            return result;
        }

    }
    public static implicit operator string(Types str)
    {
        return str.ToString();
    }

    public static bool operator ==(Types a, Types b)
    {
        return a.value == b.value;
    }
    public static bool operator !=(Types a, Types b)
    {
        return a.value != b.value;
    }

    #region enum

    public const string DataType = "Data";
    public const string ImageType = "Image";

    #endregion

}

ma ovviamente "Tipi bob = 4;" non avrebbe senso se non li avessi inizializzati per primi, il che avrebbe in qualche modo sconfitto il punto ...

Ma in teoria TypeA == TypeB sarebbe più veloce ...


3

Se ti sto capendo correttamente, puoi semplicemente usare .ToString () per recuperare il nome dell'enum dal valore (Supponendo che sia già lanciato come Enum); Se hai avuto l'int nudo (diciamo da un database o qualcosa del genere) puoi prima lanciarlo nell'enum. Entrambi i metodi seguenti ti daranno il nome enum.

AuthenticationMethod myCurrentSetting = AuthenticationMethod.FORMS;
Console.WriteLine(myCurrentSetting); // Prints: FORMS
string name = Enum.GetNames(typeof(AuthenticationMethod))[(int)myCurrentSetting-1];
Console.WriteLine(name); // Prints: FORMS

Tuttavia, tieni presente che la seconda tecnica presuppone che tu stia utilizzando ints e il tuo indice è basato su 1 (non su 0). Anche la funzione GetNames è piuttosto pesante in confronto, stai generando un intero array ogni volta che viene chiamato. Come puoi vedere nella prima tecnica, .ToString () viene effettivamente chiamato in modo implicito. Entrambi sono già menzionati nelle risposte, ovviamente, sto solo cercando di chiarire le differenze tra loro.


3

vecchio post ma ...

La risposta a questa può effettivamente essere molto semplice. Utilizzare la funzione Enum.ToString ()

Esistono 6 sovraccarichi di questa funzione, è possibile utilizzare Enum.Tostring ("F") o Enum.ToString () per restituire il valore della stringa. Non c'è bisogno di preoccuparsi di nient'altro. Ecco una demo funzionante

Nota che questa soluzione potrebbe non funzionare per tutti i compilatori ( questa demo non funziona come previsto ) ma almeno funziona per l'ultimo compilatore.



2

Bene, dopo aver letto tutto quanto sopra, sento che i ragazzi hanno complicato troppo il problema di trasformare gli enumeratori in stringhe. Mi è piaciuta l'idea di avere attributi su campi enumerati, ma penso che gli attributi vengano utilizzati principalmente per i metadati, ma nel tuo caso penso che tutto ciò di cui hai bisogno sia una sorta di localizzazione.

public enum Color 
{ Red = 1, Green = 2, Blue = 3}


public static EnumUtils 
{
   public static string GetEnumResourceString(object enumValue)
    {
        Type enumType = enumValue.GetType();
        string value = Enum.GetName(enumValue.GetType(), enumValue);
        string resourceKey = String.Format("{0}_{1}", enumType.Name, value);
        string result = Resources.Enums.ResourceManager.GetString(resourceKey);
        if (string.IsNullOrEmpty(result))
        {
            result = String.Format("{0}", value);
        }
        return result;
    }
}

Ora se proviamo a chiamare il metodo sopra possiamo chiamarlo in questo modo

public void Foo()
{
  var col = Color.Red;
  Console.WriteLine (EnumUtils.GetEnumResourceString (col));
}

Tutto quello che devi fare è solo creare un file di risorse contenente tutti i valori dell'enumeratore e le stringhe corrispondenti

Nome risorsa Valore risorsa
Color_Red My String Color in Red
Color_Blue Blueeey
Color_Green Hulk Color

La cosa davvero interessante è che sarà molto utile se hai bisogno di localizzare la tua applicazione, dal momento che tutto ciò che devi fare è semplicemente creare un altro file di risorse con la tua nuova lingua! e Voe-la!


1

Quando mi trovo in una situazione del genere, propongo la soluzione di seguito.

E come classe consumante potresti avere

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyApp.Dictionaries
{
    class Greek
    {

        public static readonly string Alpha = "Alpha";
        public static readonly string Beta = "Beta";
        public static readonly string Gamma = "Gamma";
        public static readonly string Delta = "Delta";


        private static readonly BiDictionary<int, string> Dictionary = new BiDictionary<int, string>();


        static Greek() {
            Dictionary.Add(1, Alpha);
            Dictionary.Add(2, Beta);
            Dictionary.Add(3, Gamma);
            Dictionary.Add(4, Delta);
        }

        public static string getById(int id){
            return Dictionary.GetByFirst(id);
        }

        public static int getByValue(string value)
        {
            return Dictionary.GetBySecond(value);
        }

    }
}

E usando un dizionario bidirezionale: basato su questo ( https://stackoverflow.com/a/255638/986160 ) supponendo che le chiavi saranno associate a singoli valori nel dizionario e simili a ( https://stackoverflow.com/a / 255630/986160 ) ma un po 'più elegante. Questo dizionario è anche enumerabile e puoi andare avanti e indietro da ints a stringhe. Inoltre non devi avere nessuna stringa nella tua base di codice, ad eccezione di questa classe.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace MyApp.Dictionaries
{

    class BiDictionary<TFirst, TSecond> : IEnumerable
    {
        IDictionary<TFirst, TSecond> firstToSecond = new Dictionary<TFirst, TSecond>();
        IDictionary<TSecond, TFirst> secondToFirst = new Dictionary<TSecond, TFirst>();

        public void Add(TFirst first, TSecond second)
        {
            firstToSecond.Add(first, second);
            secondToFirst.Add(second, first);
        }

        public TSecond this[TFirst first]
        {
            get { return GetByFirst(first); }
        }

        public TFirst this[TSecond second]
        {
            get { return GetBySecond(second); }
        }

        public TSecond GetByFirst(TFirst first)
        {
            return firstToSecond[first];
        }

        public TFirst GetBySecond(TSecond second)
        {
            return secondToFirst[second];
        }

        public IEnumerator GetEnumerator()
        {
            return GetFirstEnumerator();
        }

        public IEnumerator GetFirstEnumerator()
        {
            return firstToSecond.GetEnumerator();
        }

        public IEnumerator GetSecondEnumerator()
        {
            return secondToFirst.GetEnumerator();
        }
    }
}

1

Per set di enumerazioni di stringa più grandi, gli esempi elencati possono diventare noiosi. Se si desidera un elenco di codici di stato o un elenco di altri enum basati su stringhe, un sistema di attributi è fastidioso da usare e una classe statica con istanze di sé è fastidiosa da configurare. Per la mia soluzione, utilizzo il templating T4 per rendere più facile avere enumerazioni con stringhe. Il risultato è simile a come funziona la classe HttpMethod.

Puoi usarlo in questo modo:

    string statusCode = ResponseStatusCode.SUCCESS; // Automatically converts to string when needed
    ResponseStatusCode codeByValueOf = ResponseStatusCode.ValueOf(statusCode); // Returns null if not found

    // Implements TypeConverter so you can use it with string conversion methods.
    var converter = System.ComponentModel.TypeDescriptor.GetConverter(typeof(ResponseStatusCode));
    ResponseStatusCode code = (ResponseStatusCode) converter.ConvertFromInvariantString(statusCode);

    // You can get a full list of the values
    bool canIterateOverValues = ResponseStatusCode.Values.Any(); 

    // Comparisons are by value of the "Name" property. Not by memory pointer location.
    bool implementsByValueEqualsEqualsOperator = "SUCCESS" == ResponseStatusCode.SUCCESS; 

Si inizia con un file Enum.tt.

<#@ include file="StringEnum.ttinclude" #>


<#+
public static class Configuration
{
    public static readonly string Namespace = "YourName.Space";
    public static readonly string EnumName = "ResponseStatusCode";
    public static readonly bool IncludeComments = true;

    public static readonly object Nodes = new
    {
        SUCCESS = "The response was successful.",
        NON_SUCCESS = "The request was not successful.",
        RESOURCE_IS_DISCONTINUED = "The resource requested has been discontinued and can no longer be accessed."
    };
}
#>

Quindi, aggiungi il tuo file StringEnum.ttinclude.

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Reflection" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
<#@ CleanupBehavior processor="T4VSHost" CleanupAfterProcessingtemplate="true" #>

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;

namespace <#= Configuration.Namespace #>
{
    /// <summary>
    /// TypeConverter implementations allow you to use features like string.ToNullable(T).
    /// </summary>
    public class <#= Configuration.EnumName #>TypeConverter : TypeConverter
    {
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
        }

        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
        {
            var casted = value as string;

            if (casted != null)
            {
                var result = <#= Configuration.EnumName #>.ValueOf(casted);
                if (result != null)
                {
                    return result;
                }
            }

            return base.ConvertFrom(context, culture, value);
        }

        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        {
            var casted = value as <#= Configuration.EnumName #>;
            if (casted != null && destinationType == typeof(string))
            {
                return casted.ToString();
            }

            return base.ConvertTo(context, culture, value, destinationType);
        }
    }

    [TypeConverter(typeof(<#= Configuration.EnumName #>TypeConverter))]
    public class <#= Configuration.EnumName #> : IEquatable<<#= Configuration.EnumName #>>
    {
//---------------------------------------------------------------------------------------------------
// V A L U E S _ L I S T
//---------------------------------------------------------------------------------------------------
<# Write(Helpers.PrintEnumProperties(Configuration.Nodes)); #>

        private static List<<#= Configuration.EnumName #>> _list { get; set; } = null;
        public static List<<#= Configuration.EnumName #>> ToList()
        {
            if (_list == null)
            {
                _list = typeof(<#= Configuration.EnumName #>).GetFields().Where(x => x.IsStatic && x.IsPublic && x.FieldType == typeof(<#= Configuration.EnumName #>))
                    .Select(x => x.GetValue(null)).OfType<<#= Configuration.EnumName #>>().ToList();
            }

            return _list;
        }

        public static List<<#= Configuration.EnumName #>> Values()
        {
            return ToList();
        }

        /// <summary>
        /// Returns the enum value based on the matching Name of the enum. Case-insensitive search.
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static <#= Configuration.EnumName #> ValueOf(string key)
        {
            return ToList().FirstOrDefault(x => string.Compare(x.Name, key, true) == 0);
        }


//---------------------------------------------------------------------------------------------------
// I N S T A N C E _ D E F I N I T I O N
//---------------------------------------------------------------------------------------------------      
        public string Name { get; private set; }
        public string Description { get; private set; }
        public override string ToString() { return this.Name; }

        /// <summary>
        /// Implcitly converts to string.
        /// </summary>
        /// <param name="d"></param>
        public static implicit operator string(<#= Configuration.EnumName #> d)
        {
            return d.ToString();
        }

        /// <summary>
        /// Compares based on the == method. Handles nulls gracefully.
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static bool operator !=(<#= Configuration.EnumName #> a, <#= Configuration.EnumName #> b)
        {
            return !(a == b);
        }

        /// <summary>
        /// Compares based on the .Equals method. Handles nulls gracefully.
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static bool operator ==(<#= Configuration.EnumName #> a, <#= Configuration.EnumName #> b)
        {
            return a?.ToString() == b?.ToString();
        }

        /// <summary>
        /// Compares based on the .ToString() method
        /// </summary>
        /// <param name="o"></param>
        /// <returns></returns>
        public override bool Equals(object o)
        {
            return this.ToString() == o?.ToString();
        }

        /// <summary>
        /// Compares based on the .ToString() method
        /// </summary>
        /// <param name="other"></param>
        /// <returns></returns>
        public bool Equals(<#= Configuration.EnumName #> other)
        {
            return this.ToString() == other?.ToString();
        }

        /// <summary>
        /// Compares based on the .Name property
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return this.Name.GetHashCode();
        }
    }
}

<#+

public static class Helpers
{
        public static string PrintEnumProperties(object nodes)
        {
            string o = "";
            Type nodesTp = Configuration.Nodes.GetType();
            PropertyInfo[] props = nodesTp.GetProperties().OrderBy(p => p.Name).ToArray();

            for(int i = 0; i < props.Length; i++)
            {
                var prop = props[i];
                if (Configuration.IncludeComments)
                {
                    o += "\r\n\r\n";
                    o += "\r\n        ///<summary>";
                    o += "\r\n        /// "+Helpers.PrintPropertyValue(prop, Configuration.Nodes);
                    o += "\r\n        ///</summary>";
                }

                o += "\r\n        public static readonly "+Configuration.EnumName+" "+prop.Name+ " = new "+Configuration.EnumName+"(){ Name = \""+prop.Name+"\", Description = "+Helpers.PrintPropertyValue(prop, Configuration.Nodes)+ "};";
            }

            o += "\r\n\r\n";

            return o;
        }

        private static Dictionary<string, string> GetValuesMap()
        {
            Type nodesTp = Configuration.Nodes.GetType();
            PropertyInfo[] props= nodesTp.GetProperties();
            var dic = new Dictionary<string,string>();
            for(int i = 0; i < props.Length; i++)
            {
                var prop = nodesTp.GetProperties()[i];
                dic[prop.Name] = prop.GetValue(Configuration.Nodes).ToString();
            }
            return dic;
        }

        public static string PrintMasterValuesMap(object nodes)
        {
            Type nodesTp = Configuration.Nodes.GetType();
            PropertyInfo[] props= nodesTp.GetProperties();
            string o = "        private static readonly Dictionary<string, string> ValuesMap = new Dictionary<string, string>()\r\n        {";
            for(int i = 0; i < props.Length; i++)
            {
                var prop = nodesTp.GetProperties()[i];
                o += "\r\n            { \""+prop.Name+"\", "+(Helpers.PrintPropertyValue(prop,Configuration.Nodes)+" },");
            }
            o += ("\r\n        };\r\n");

            return o;
        }


        public static string PrintPropertyValue(PropertyInfo prop, object objInstance)
        {
            switch(prop.PropertyType.ToString()){
                case "System.Double":
                    return prop.GetValue(objInstance).ToString()+"D";
                case "System.Float":
                    return prop.GetValue(objInstance).ToString()+"F";
                case "System.Decimal":
                    return prop.GetValue(objInstance).ToString()+"M";
                case "System.Long":
                    return prop.GetValue(objInstance).ToString()+"L";
                case "System.Boolean":
                case "System.Int16":
                case "System.Int32":
                    return prop.GetValue(objInstance).ToString().ToLowerInvariant();
                case "System.String":
                    return "\""+prop.GetValue(objInstance)+"\"";
            }

            return prop.GetValue(objInstance).ToString();
        }

        public static string _ (int numSpaces)
        {
            string o = "";
            for(int i = 0; i < numSpaces; i++){
                o += " ";
            }

            return o;
        }
}
#>

Infine, ricompilare il file Enum.tt e l'output è simile al seguente:

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

using System;
using System.Linq;
using System.Collections.Generic;

namespace YourName.Space
{
    public class ResponseStatusCode
    {
//---------------------------------------------------------------------------------------------------
// V A L U E S _ L I S T 
//---------------------------------------------------------------------------------------------------



        ///<summary>
        /// "The response was successful."
        ///</summary>
        public static readonly ResponseStatusCode SUCCESS = new ResponseStatusCode(){ Name = "SUCCESS", Description = "The response was successful."};


        ///<summary>
        /// "The request was not successful."
        ///</summary>
        public static readonly ResponseStatusCode NON_SUCCESS = new ResponseStatusCode(){ Name = "NON_SUCCESS", Description = "The request was not successful."};


        ///<summary>
        /// "The resource requested has been discontinued and can no longer be accessed."
        ///</summary>
        public static readonly ResponseStatusCode RESOURCE_IS_DISCONTINUED = new ResponseStatusCode(){ Name = "RESOURCE_IS_DISCONTINUED", Description = "The resource requested has been discontinued and can no longer be accessed."};


        private static List<ResponseStatusCode> _list { get; set; } = null;
        public static List<ResponseStatusCode> ToList()
        {
            if (_list == null)
            {
                _list = typeof(ResponseStatusCode).GetFields().Where(x => x.IsStatic && x.IsPublic && x.FieldType == typeof(ResponseStatusCode))
                    .Select(x => x.GetValue(null)).OfType<ResponseStatusCode>().ToList();
            }

            return _list;
        }

        public static List<ResponseStatusCode> Values()
        {
            return ToList();
        }

        /// <summary>
        /// Returns the enum value based on the matching Name of the enum. Case-insensitive search.
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public static ResponseStatusCode ValueOf(string key)
        {
            return ToList().FirstOrDefault(x => string.Compare(x.Name, key, true) == 0);
        }


//---------------------------------------------------------------------------------------------------
// I N S T A N C E _ D E F I N I T I O N 
//---------------------------------------------------------------------------------------------------       
        public string Name { get; set; }
        public string Description { get; set; }
        public override string ToString() { return this.Name; }

        /// <summary>
        /// Implcitly converts to string.
        /// </summary>
        /// <param name="d"></param>
        public static implicit operator string(ResponseStatusCode d)
        {
            return d.ToString();
        }

        /// <summary>
        /// Compares based on the == method. Handles nulls gracefully.
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static bool operator !=(ResponseStatusCode a, ResponseStatusCode b)
        {
            return !(a == b);
        }

        /// <summary>
        /// Compares based on the .Equals method. Handles nulls gracefully.
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        /// <returns></returns>
        public static bool operator ==(ResponseStatusCode a, ResponseStatusCode b)
        {
            return a?.ToString() == b?.ToString();
        }

        /// <summary>
        /// Compares based on the .ToString() method
        /// </summary>
        /// <param name="o"></param>
        /// <returns></returns>
        public override bool Equals(object o)
        {
            return this.ToString() == o?.ToString();
        }

        /// <summary>
        /// Compares based on the .Name property
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return this.Name.GetHashCode();
        }
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.