c# - Rappresentazione in serie di un Enum



15 Answers

Utilizzare il metodo

Enum.GetName(Type MyEnumType,  object enumvariable)  

come in (Assume Shipper è 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 investigare anche ...

c# enums

Ho il seguente elenco:

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

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

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

Per prima cosa ho bisogno di 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 quello 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 stringa per un enumeratore. Posso quindi usarlo in questo modo:

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

Okay, ora funzionano tutti come un incantesimo ma trovo molto lavoro. Mi stavo chiedendo se c'è una soluzione migliore per questo.

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




Purtroppo la riflessione per ottenere attributi sull'enumerazione è piuttosto lenta:

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

Anche .ToString() è piuttosto lento sulle enumerazioni.

Tuttavia, puoi scrivere i metodi di estensione per enumerati:

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

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

Aggiornamento C # 6

Se è possibile utilizzare C # 6, il nuovo operatore nameof funziona per enumerazioni, quindi nameof(MyEnum.WINDOWSAUTHENTICATION) verrà convertito in "WINDOWSAUTHENTICATION" in fase di compilazione , rendendolo il modo più rapido per ottenere i nomi enum.

Nota che questo convertirà l'enum esplicito in una costante in linea, quindi non funziona per le enumerazioni che hai in una variabile. Così:

nameof(AuthenticationMethod.FORMS) == "FORMS"

Ma...

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



Basta usare il metodo ToString()

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

Per fare riferimento alla stringa Tomato , basta usare

any.Tomato.ToString();



Soluzione molto semplice con .Net 4.0 e versioni successive. Non è necessario nessun 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" di sopra) quando si chiama Enum.ToString vedere questa pagina Stringhe di formato di enumerazione




Uso una combinazione di molti dei suggerimenti sopra, combinati con alcuni caching. Ora, ho avuto l'idea da un codice che ho trovato da qualche parte sulla rete, ma non riesco a ricordare dove l'ho preso o trovato. Quindi, se qualcuno trova qualcosa che sembra simile, per favore commenta con l'attribuzione.

Ad ogni modo, l'utilizzo coinvolge i convertitori di tipi, quindi se si sta vincolando all'interfaccia utente 'funziona'. È possibile estendere con il pattern di Jakub per la ricerca rapida 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
}

Il codice per il convertitore di tipo enum personalizzato segue:

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

}




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

Se non lo fai e hai solo bisogno di un enum di tipo string (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 farvi riferimento

if (bla == AuthenticationMethod.FORMS)

Sarà un po 'più lento rispetto ai valori numerici (confrontando le stringhe al posto dei numeri) ma sul lato positivo non sta usando il reflection (slow) per accedere alla stringa.




Come ho risolto questo come un 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
}

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

o.OrderType.GetDescription()

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




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

  • I nomi dei miei valori enumerati sono sufficientemente amichevoli per lo scopo, o devo fornire dei più amichevoli?
  • Devo fare il viaggio di andata e ritorno? Cioè, dovrò prendere i valori del testo e analizzarli in valori enum?
  • È qualcosa che devo fare per molte enumerazioni nel mio progetto, o solo uno?
  • In che tipo di elementi dell'interfaccia utente presenterò queste informazioni - in particolare, sarò vincolante per l'interfaccia utente o utilizzando le finestre delle proprietà?
  • Questo deve essere localizzabile?

Il modo più semplice per eseguire questa operazione è con Enum.GetValue (e supporta il round-trip usando Enum.Parse ). Vale anche la pena di costruire un TypeConverter , come suggerisce Steve Mitcham, per supportare l'UI binding. (Non è necessario creare un TypeConverter quando si usano i fogli di proprietà, che è una delle cose carine sui fogli delle proprietà, anche se il signore sa di avere i propri problemi).

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




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 necessario un sacco di tali enumerazioni, potrebbe essere utilizzata la generazione del codice (ad esempio T4).




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 certo numero di valori; in altre parole, per definire una classe.

L'enum pattern di Jakub Šturc è la migliore opzione che vedo qui.

Guardarlo:

  • Ha un costruttore privato quindi solo la classe stessa può definire i valori consentiti.
  • È una classe sigillata in modo che i valori non possano essere modificati tramite l'ereditarietà.
  • È sicuro dal tipo, 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 Nome, Descrizione e Valore numerico.



Ecco un altro modo per svolgere il compito di associare stringhe con enumerazioni:

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 è chiamato così:

public FormMain() {
    DATABASE dbEnum;

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

È possibile raggruppare le enumerazioni correlate nella propria struttura. Poiché questo metodo utilizza il tipo enum, è possibile utilizzare Intellisense per visualizzare l'elenco di enumerazioni quando si effettua la GetString()chiamata.

Puoi opzionalmente usare il nuovo operatore sulla DATABASEstruct. Non usarlo significa che le stringhe Listnon vengono allocate fino a quando non GetString()viene effettuata la prima chiamata.




Se ti sto capendo correttamente, puoi semplicemente usare .ToString () per recuperare il nome dell'enum dal valore (Supponendo che sia già stato lanciato come Enum); Se tu avessi l'int nuda (diciamo da un database o qualcosa del genere), puoi prima lanciarla all'enum. Entrambi i metodi seguenti ti daranno il nome dell'enumerazione.

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 gli ints e che l'indice sia basato su 1 (non basato su 0). La funzione GetNames è anche piuttosto pesante al confronto, stai generando un intero array ogni volta che viene chiamato. Come puoi vedere nella prima tecnica, .ToString () è in realtà chiamato implicitamente. Entrambe queste sono già citate nelle risposte, naturalmente, sto solo cercando di chiarire le differenze tra loro.







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

E come una classe consumatrice che 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://.com/a/255638/986160 ) assumendo che le chiavi saranno associate a singoli valori nel dizionario e simili a ( https://.com/a/255630/986160 ) ma un po 'più elegante. Questo dizionario è anche enumerabile e puoi andare avanti e indietro dagli interi alle stringhe. Inoltre non devi avere alcuna stringa nella base di codice con l'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();
        }
    }
}



La mia risposta, lavorando sulla risposta di @ user29964 (che è di gran lunga la più semplice e la più vicina ad una Enum) è

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

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

        public string Value
        {
            get { return _value; }
        }



        public static string GetStringValue(Enum Flagvalue)
        {
            Type type = Flagvalue.GetType();
            string[] flags = Flagvalue.ToString().Split(',').Select(x => x.Trim()).ToArray();
            List<string> values = new List<string>();

            for (int i = 0; i < flags.Length; i++)
            {

                FieldInfo fi = type.GetField(flags[i].ToString());

                StringValue[] attrs =
                   fi.GetCustomAttributes(typeof(StringValue),
                                           false) as StringValue[];
                if (attrs.Length > 0)
                {
                    values.Add(attrs[0].Value);
                }
            }
            return String.Join(",", values);

        }

uso

[Flags]
    public enum CompeteMetric
    {

        /// <summary>
        /// u
        /// </summary>
        [StringValue("u")]//Json mapping
        Basic_UniqueVisitors = 1 //Basic
             ,
        /// <summary>
        /// vi
        /// </summary>
        [StringValue("vi")]//json mapping
        Basic_Visits = 2// Basic
            ,
        /// <summary>
        /// rank
        /// </summary>
        [StringValue("rank")]//json mapping
        Basic_Rank = 4//Basic
 }

Esempio

        CompeteMetric metrics = CompeteMetric.Basic_Visits | CompeteMetric.Basic_Rank;
        string strmetrics = StringValue.GetStringValue(metrics);

questo restituirà "vi, rank"




Related


Tags

c#   enums