Associare enum con stringhe in C #


363

So che il seguente non è possibile perché il tipo di enumerazione deve essere un int

enum GroupTypes
{
    TheGroup = "OEM",
    TheOtherGroup = "CMB"
}

Dal mio database ottengo un campo con codici incomprensibili (il OEMeCMB ). Vorrei trasformare questo campo in enumqualcosa o qualcos'altro comprensibile. Perché se l'obiettivo è la leggibilità, la soluzione dovrebbe essere concisa.

Quali altre opzioni ho?


possibile duplicato di Enum ToString
nawfal

12
Non sono sicuro del motivo per cui la maggior parte delle risposte non utilizza solo "const string" e invece crea classi personalizzate.
CTS_AE,

1
Potresti non essere in grado di usare le stringhe, ma puoi usare i caratteri bene. Questa è un'opzione se puoi usare valori a lettera singola.
T. Sar,

1
Sinceramente confuso sul perché la soluzione proposta sopra da CTS_AE non sia nemmeno tra le prime tre risposte.
Sinjai,

@Sinjai Il raggruppamento esplicito di valori correlati supererà la penalità di una perdita di prestazioni impercettibile, specialmente in un'API o in un componente riutilizzabile.
persona27

Risposte:


404

Mi piace usare le proprietà in una classe anziché i metodi, dal momento che sembrano più simili a enum.

Ecco un esempio per un logger:

public class LogCategory
{
    private LogCategory(string value) { Value = value; }

    public string Value { get; set; }

    public static LogCategory Trace   { get { return new LogCategory("Trace"); } }
    public static LogCategory Debug   { get { return new LogCategory("Debug"); } }
    public static LogCategory Info    { get { return new LogCategory("Info"); } }
    public static LogCategory Warning { get { return new LogCategory("Warning"); } }
    public static LogCategory Error   { get { return new LogCategory("Error"); } }
}

Passa i valori di stringa di sicurezza come parametro:

public static void Write(string message, LogCategory logCategory)
{
    var log = new LogEntry { Message = message };
    Logger.Write(log, logCategory.Value);
}

Uso:

Logger.Write("This is almost like an enum.", LogCategory.Info);

4
L'unico lato negativo che posso escogitare è che sarebbe un po 'più lento, ma nella maggior parte dei casi sarebbe trascurabile. E non avrebbe lo stesso identico comportamento nell'editor. Ad esempio: la commutazione su questo non riempirebbe automaticamente un caso per ogni possibilità. A parte questi punti minori, penso che questa sia probabilmente una soluzione piuttosto semplice.
Boris Callens,

3
Ed è facile usare Dizionario <LogCategory, Action / Func> come interruttore. :)
Arnis Lapsa il

4
@ArnisL. Non è sufficiente per funzionare come chiave, è necessario sovrascrivere Equals () e GetHashCode () e si desidera rendere privato il setter della proprietà Value. Tuttavia, non è un enum.
Dave Van den Eynde,

21
Per mio uso, ho ampliato questo concetto, scavalcando il ToStringmetodo per tornare Value. E quindi fornito operatori cast impliciti da e verso una stringa. public static implicit operator String(LogCategory category) { return Value; }.
Zarepheth,

6
Che ne dici di usarlo in casi switch?
David

175

È inoltre possibile utilizzare il modello di estensione:

public enum MyEnum
{
    [Description("String 1")]
    V1= 1,
    [Description("String 2")]
    V2= 2
} 

La tua classe di estensione

public static class MyEnumExtensions
{
    public static string ToDescriptionString(this MyEnum val)
    {
        DescriptionAttribute[] attributes = (DescriptionAttribute[])val
           .GetType()
           .GetField(val.ToString())
           .GetCustomAttributes(typeof(DescriptionAttribute), false);
        return attributes.Length > 0 ? attributes[0].Description : string.Empty;
    }
} 

utilizzo:

MyEnum myLocal = MyEnum.V1;
print(myLocal.ToDescriptionString());

3
Vedi anche stackoverflow.com/questions/4367723/… per un'altra estensione e da stringa a enum a titolo di descrizione.
Dave,

15
Non posso fare a meno di pensare che riflettere l'enum ogni volta che vuoi che mostri il testo suona un po 'doloroso dal punto di vista della performance!
Liath,

4
@Liath - Il `.ToString ()` usa già la riflessione, quindi non stai davvero perdendo nulla con questo approccio e acquisendo leggibilità
James King

1
Potresti rendere l'estensione generica in modo che si applichi automaticamente a tutti gli Enum?
Erosebe,

3
Per rendere generico, usa public static string ToDescriptionString(this Enum ...cioè senza digitare esplicitamente a MyEnum.
LeeCambl,

100

Che ne dici di usare una classe statica con costanti?

static class GroupTypes
{
  public const string TheGroup = "OEM";
  public const string TheOtherGroup = "CMB";
}

void DoSomething(string groupType)
{
  if(groupType == GroupTypes.TheGroup)
  {
    // Be nice
  }  
  else if (groupType == GroupTypes.TheOtherGroup)
  {
    // Continue to be nice
  }
  else
  {
    // unexpected, throw exception?
  }
}

9
Concordato. Ho problemi a vedere lo scopo dietro le soluzioni più complesse, tranne forse per essere in grado di cambiare "enum" risultante.
fakeleft,

@fakeleft non puoi usare un tipo di classe statica con un tipo generico (template), e forse altre limitazioni, penso sia per questo che la gente preferisce le soluzioni "più complesse".
eselk,

2
Tuttavia, le costanti devono essere interne o pubbliche perché funzioni
arviman,

46
I tipi statici non possono essere utilizzati come parametri.
Pedro Moreira,

2
Come sottolinea @PedroMoreira, non è possibile passare GroupTypescome tipo di argomento perché è una classe statica. Questo è il problema che anche la risposta di Mien risolve. In questo caso dovresti invece avere void DoSomething(string groupType), il che significa che groupTypepotrebbe avere qualsiasi valore di stringa , anche i valori che non ti aspetti, il che significa che devi essere preparato per quei tipi non validi e decidere come gestirli (ad es. lanciando un'eccezione). Anche la risposta di Mien risolve ciò limitando il numero di input validi alle opzioni definite dalla LogCategoryclasse.
Pharap,

30

È possibile aggiungere attributi agli elementi nell'enumerazione e quindi utilizzare la riflessione per ottenere i valori dagli attributi.

Dovresti usare l'identificatore "field" per applicare gli attributi, in questo modo:

enum GroupTypes
{
    [field:Description("OEM")]
    TheGroup,

    [field:Description("CMB")]
    TheOtherGroup
}

Dovresti quindi riflettere sui campi statici del tipo di enum (in questo caso GroupTypes) e ottenere DescriptionAttributeil valore che stavi cercando usando reflection:

public static DescriptionAttribute GetEnumDescriptionAttribute<T>(
    this T value) where T : struct
{
    // The type of the enum, it will be reused.
    Type type = typeof(T);

    // If T is not an enum, get out.
    if (!type.IsEnum) 
        throw new InvalidOperationException(
            "The type parameter T must be an enum type.");

    // If the value isn't defined throw an exception.
    if (!Enum.IsDefined(type, value))
        throw new InvalidEnumArgumentException(
            "value", Convert.ToInt32(value), type);

    // Get the static field for the value.
    FieldInfo fi = type.GetField(value.ToString(), 
        BindingFlags.Static | BindingFlags.Public);

    // Get the description attribute, if there is one.
    return fi.GetCustomAttributes(typeof(DescriptionAttribute), true).
        Cast<DescriptionAttribute>().SingleOrDefault();
}

Ho scelto di restituire lo DescriptionAttributestesso sopra, nel caso in cui tu voglia essere in grado di determinare se l'attributo viene applicato o meno.


Anche se lo ricorderò per situazioni più complesse, è piuttosto complesso per una situazione con il livello di complessità di ciò che ho affermato nel PO
Boris Callens,

26

Puoi farlo molto facilmente in realtà. Usa il seguente codice.

enum GroupTypes
{
   OEM,
   CMB
};

Quindi, quando vuoi ottenere il valore di stringa di ogni elemento enum, usa solo la seguente riga di codice.

String oemString = Enum.GetName(typeof(GroupTypes), GroupTypes.OEM);

Ho usato questo metodo con successo in passato e ho anche usato una classe di costanti per contenere costanti di stringa, entrambe funzionano abbastanza bene, ma tendo a preferire questo.


Stavo pensando la stessa cosa, ma ci deve essere un po 'di cattura in questo ... Altrimenti sospetto che più persone lo suggerirebbero (forse sono solo paranoico).
Matthijs Wessels l'

L'unica cattura di cui sono consapevole è che credo che usi la riflessione per capire la stringa. Di conseguenza, se sto cercando una soluzione per tenere traccia di una stringa costante, in genere userò una classe per memorizzare la maggior parte delle mie stringhe costanti. Tuttavia, se ho una situazione in cui un Enum è la soluzione giusta (a prescindere dall'ottenere una stringa descrittiva sui miei elementi Enum), allora invece di avere una stringa aggiuntiva che fluttua da qualche parte per gestire, uso semplicemente il valore enum come descritto.
Arthur C,

1 Questa è la risposta migliore e più semplice, e ha anche alti voti qui per dimostrarlo. L'unica volta in cui è meglio usare il modello di estensione è quando hai bisogno di spazi nel testo (maggiori dettagli qui ).
SharpC

14
No, si tratta solo di ottenere il nome di un valore enum, non di assegnare una stringa a un valore enum. L'obiettivo dell'OP è di avere una stringa diversa dal valore enum, ad esempio: TheGroup = "OEM", TheOtherGroup = "CMB".
Tim Autin,

3
Concordo con il commento di @ Tim, questo non è ciò che l'OP sta cercando di fare. Se ti stai chiedendo quale sia un caso d'uso di questo, considera una situazione in cui un dispositivo prende le stringhe come comandi, ma deve esserci anche una versione "leggibile dall'uomo" del comando. Ne avevo bisogno per associare qualcosa come "Aggiorna firmware" con il comando "UPDATEFW".
JYelton,

20

Prova ad aggiungere costanti a una classe statica. Non finisci con un Tipo, ma avrai costanti organizzate e leggibili:

public static class GroupTypes {

    public const string TheGroup = "OEM";
    public const string TheOtherGroup = "CMB";

}

3
Difficile tornare dal codice al nome descrittivo. Dovresti usare la riflessione su tutti i campi const per cercare una corrispondenza.
andleer,

1
@andleer Non capisco la tua preoccupazione. Questa è la soluzione che uso.
VSO,

Sì, questo è esattamente quello che volevo. E questa è la soluzione più concisa / elegante che vedo, proprio come se stessi definendo un elenco con valori int - ma con valori stringa invece. 100% perfetto.
Chad,

3
Il problema è che non funziona come Enum, nel senso che non avremo un tipo separato con un elenco finito di valori. Una funzione in attesa di questi potrebbe essere utilizzata con stringhe in formato libero che sono soggette a errori.
Juan Martinez,

14

Crea un secondo enum, per il tuo DB contenente quanto segue:

enum DBGroupTypes
{
    OEM = 0,
    CMB = 1
}

Ora puoi utilizzare Enum.Parse per recuperare il valore DBGroupTypes corretto dalle stringhe "OEM" e "CMB". È quindi possibile convertirli in int e recuperare i valori corretti dall'enumerazione corretta che si desidera utilizzare ulteriormente nel modello.


Questo sembra essere un ulteriore passo nel processo, perché non una classe che gestisce tutto?
C. Ross,

11
Al contrario dell'uso di attributi e riflessioni?
Dave Van den Eynde,

13

Usa una lezione.

Modifica: esempio migliore

class StarshipType
{
    private string _Name;
    private static List<StarshipType> _StarshipTypes = new List<StarshipType>();

    public static readonly StarshipType Ultralight = new StarshipType("Ultralight");
    public static readonly StarshipType Light = new StarshipType("Light");
    public static readonly StarshipType Mediumweight = new StarshipType("Mediumweight");
    public static readonly StarshipType Heavy = new StarshipType("Heavy");
    public static readonly StarshipType Superheavy = new StarshipType("Superheavy");

    public string Name
    {
        get { return _Name; }
        private set { _Name = value; }
    }

    public static IList<StarshipType> StarshipTypes
    {
        get { return _StarshipTypes; }
    }

    private StarshipType(string name, int systemRatio)
    {
        Name = name;
        _StarshipTypes.Add(this);
    }

    public static StarshipType Parse(string toParse)
    {
        foreach (StarshipType s in StarshipTypes)
        {
            if (toParse == s.Name)
                return s;
        }
        throw new FormatException("Could not parse string.");
    }
}

1
Difficile tornare dal codice al nome descrittivo. Dovresti usare la riflessione su tutti i campi const per cercare una corrispondenza.
andleer,

1
Vedo il tuo punto. Caricherò una versione che funziona più tardi in seguito, ma ammetto che è piuttosto pesante.
C. Ross,

La mia versione basata sulla soluzione di C. Ross stackoverflow.com/a/48441114/3862615
Roman M

7

Un altro modo per affrontare il problema è avere un enum e una matrice di stringhe che mapperanno i valori di enum con l'elenco di stringhe:

public enum GroupTypes
{
    TheGroup  = 0,
    TheOtherGroup 
}

string[] GroupTypesStr = {
    "OEM",
    "CMB"
};

puoi usarlo in questo modo:

Log.Write(GroupTypesStr[(int)GroupTypes.TheOtherGroup]);

Richiederà CMB

PROFESSIONISTI:

  1. Codice facile e pulito.
  2. Alte prestazioni (specialmente rispetto a quegli approcci che utilizzano le classi)

CONS:

  1. È incline a rovinare l'elenco durante la modifica, ma andrà bene per un breve elenco.

6

Ecco il metodo di estensione che ho usato per ottenere il valore enum come stringa. Il primo qui è l'enum.

public enum DatabaseEnvironment
{
    [Description("AzamSharpBlogDevDatabase")]
    Development = 1, 
    [Description("AzamSharpBlogQADatabase")]
    QualityAssurance = 2, 
    [Description("AzamSharpBlogTestDatabase")] 
    Test = 3
}

L'attributo Descrizione proviene da System.ComponentModel.

Ed ecco il mio metodo di estensione:

public static string GetValueAsString(this DatabaseEnvironment environment) 
{
    // get the field 
    var field = environment.GetType().GetField(environment.ToString());
    var customAttributes = field.GetCustomAttributes(typeof (DescriptionAttribute), false);

    if(customAttributes.Length > 0)
    {
        return (customAttributes[0] as DescriptionAttribute).Description;  
    }
    else
    {
        return environment.ToString(); 
    }
}

Ora puoi accedere all'enum come valore stringa usando il seguente codice:

[TestFixture]
public class when_getting_value_of_enum
{
    [Test]
    public void should_get_the_value_as_string()
    {
        Assert.AreEqual("AzamSharpBlogTestDatabase",DatabaseEnvironment.Test.GetValueAsString());  
    }
}

5

Hai considerato una tabella di ricerca utilizzando un dizionario?

enum GroupTypes
{
    TheGroup,
    TheOtherGroup
}

Dictionary<string, GroupTypes> GroupTypeLookup = new Dictionary<string, GroupTypes>();
// initialize lookup table:
GroupTypeLookup.Add("OEM", TheGroup);
GroupTypeLookup.Add("CMB", TheOtherGroup);

È quindi possibile utilizzare GroupTypeLookup.TryGetValue () per cercare una stringa durante la lettura.


Come si ottiene facilmente la chiave per un determinato valore?
Eglasio,

La domanda non ha chiesto di andare dall'altra parte. Ma sarebbe abbastanza semplice costruire un altro dizionario che vada dall'altra parte. Cioè, Dizionario <GroupTypes, string>.
Jim Mischel,

4
public class DataType
{
    private readonly string value;
    private static readonly Dictionary<string, DataType> predefinedValues;

    public static readonly DataType Json = new DataType("json");
    public static readonly DataType Xml = new DataType("xml");
    public static readonly DataType Text = new DataType("text");
    public static readonly DataType Html = new DataType("html");
    public static readonly DataType Binary = new DataType("binary");

    static DataType()
    {
        predefinedValues = new Dictionary<string, DataType>();
        predefinedValues.Add(Json.Value, Json);
        predefinedValues.Add(Xml.Value, Xml);
        predefinedValues.Add(Text.Value, Text);
        predefinedValues.Add(Html.Value, Html);
        predefinedValues.Add(Binary.Value, Binary);
    }

    private DataType(string value)
    {
        this.value = value;
    }

    public static DataType Parse(string value)
    {
        var exception = new FormatException($"Invalid value for type {nameof(DataType)}");
        if (string.IsNullOrEmpty(value))
            throw exception;

        string key = value.ToLower();
        if (!predefinedValues.ContainsKey(key))
            throw exception;

        return predefinedValues[key];
    }

    public string Value
    {
        get { return value; }
    }
}

3

C # non supporta le stringhe enumerate, ma per la maggior parte delle situazioni è possibile utilizzare un Elenco o Dizionario per ottenere l'effetto desiderato.

Ad esempio, per stampare i risultati pass / fail:

List<string> PassFail = new List<string> { "FAIL", "PASS" };
bool result = true;
Console.WriteLine("Test1: " + PassFail[result.GetHashCode()]);

2

Lo trasformerei in una classe per evitare del tutto un enum. E poi con l'uso di un typehandler potresti creare l'oggetto quando lo prendi dal db.

IE:

public class Group
{
    public string Value{ get; set; }
    public Group( string value ){ Value = value; } 
    public static Group TheGroup() { return new Group("OEM"); }
    public static Group OtherGroup() { return new Group("CMB"); }

}

2

Vorrei solo creare un dizionario e utilizzare il codice come chiave.

Modifica: per rispondere al commento sull'esecuzione di una ricerca inversa (trovare la chiave), ciò non sarebbe terribilmente efficiente. Se questo è necessario, scriverei una nuova classe per gestirla.


Inoltre puoi facilmente prendere una chiave per un dato valore?
Eglasio,

Per C.Ross - Non sono sicuro di cosa intendi. È possibile leggere i valori da un db e popolare dinamicamente il dizionario.
jhale,

2

La mia prima domanda: hai accesso al database stesso? Questo dovrebbe essere normalizzato nel database, idealmente, altrimenti, qualsiasi soluzione sarà soggetta a errori. Nella mia esperienza, i campi di dati pieni di "OEM" e "CMB" tendono a finire con cose come "oem" e altri "dati di merda" mescolati nel tempo .... Se puoi normalizzarlo, puoi usare il tasto nella tabella contenente gli elementi come Enum e il gioco è fatto, con una struttura molto più pulita.

Se questo non è disponibile, farei il tuo Enum e creerei una classe per analizzare la tua stringa nell'Enum per te. Questo ti darebbe almeno una certa flessibilità nella gestione delle voci non standard e molta più flessibilità per intrappolare o gestire gli errori rispetto a qualsiasi soluzione alternativa usando Enum.Parse / Reflection / etc. Un dizionario funzionerebbe, ma potrebbe rompersi in caso di problemi, ecc.

Consiglierei di scrivere un corso in modo da poter fare:

// I renamed this to GroupType, since it sounds like each element has a single type...
GroupType theType = GroupTypeParser.GetGroupType(theDBString);

Ciò preserva la maggior parte della tua leggibilità senza dover modificare il DB.


2

Se ho capito bene, hai bisogno di una conversione da stringa a enum:

enum GroupTypes {
    Unknown = 0,
    OEM = 1,
    CMB = 2
}
static GroupTypes StrToEnum(string str){
    GroupTypes g = GroupTypes.Unknown;
    try {
        object o = Enum.Parse(typeof(GroupTypes), str, true);
        g = (GroupTypes)(o ?? 0);
    } catch {
    }
    return g;
}
// then use it like this
GroupTypes g1 = StrToEnum("OEM");
GroupTypes g2 = StrToEnum("bad value");

Puoi renderlo più elegante con i generici per il tipo di enum, se lo desideri.


2

In VS 2015, puoi usare nameof

public class LogCategory
{
    public static string Trace;
    public static string Debug;
    public static string Info;
    public static string Warning;
    public static string Error;
}

Uso:

Logger.Write("This is almost like an enum.", nameof(LogCategory.Info));

2

Un piccolo aggiustamento al metodo dell'estensione glennulare, in modo da poter usare l'estensione su altre cose oltre a quelle di ENUM;

using System;
using System.ComponentModel;
namespace Extensions {
    public static class T_Extensions {
        /// <summary>
        /// Gets the Description Attribute Value
        /// </summary>
        /// <typeparam name="T">Entity Type</typeparam>
        /// <param name="val">Variable</param>
        /// <returns>The value of the Description Attribute or an Empty String</returns>
        public static string Description<T>(this T t) {
            DescriptionAttribute[] attributes = (DescriptionAttribute[])t.GetType().GetField(t.ToString()).GetCustomAttributes(typeof(DescriptionAttribute), false);
            return attributes.Length > 0 ? attributes[0].Description : string.Empty;
        }
    }
}

O usando Linq

using System;
using System.ComponentModel;
using System.Linq;

namespace Extensions {


public static class T_Extensions {
        public static string Description<T>(this T t) =>
            ((DescriptionAttribute[])t
            ?.GetType()
            ?.GetField(t?.ToString())
            ?.GetCustomAttributes(typeof(DescriptionAttribute), false))
            ?.Select(a => a?.Description)
            ?.FirstOrDefault() 
            ?? string.Empty;  
    }
}

2

Seguendo la risposta di @Even Mien ho provato ad andare un po 'oltre e renderlo generico, mi sembra di essere quasi arrivato ma un caso ancora resiste e probabilmente posso semplificare un po' il mio codice.
Lo pubblico qui se qualcuno vede come potrei migliorare e soprattutto farlo funzionare perché non posso assegnarlo da una stringa

Finora ho i seguenti risultati:

        Console.WriteLine(TestEnum.Test1);//displays "TEST1"

        bool test = "TEST1" == TestEnum.Test1; //true

        var test2 = TestEnum.Test1; //is TestEnum and has value

        string test3 = TestEnum.Test1; //test3 = "TEST1"

        var test4 = TestEnum.Test1 == TestEnum.Test2; //false
         EnumType<TestEnum> test5 = "TEST1"; //works fine

        //TestEnum test5 = "string"; DOESN'T compile .... :(:(

Dove accade la magia:

public abstract  class EnumType<T>  where T : EnumType<T>   
{

    public  string Value { get; set; }

    protected EnumType(string value)
    {
        Value = value;
    }


    public static implicit operator EnumType<T>(string s)
    {
        if (All.Any(dt => dt.Value == s))
        {
            Type t = typeof(T);

            ConstructorInfo ci = t.GetConstructor(BindingFlags.Instance | BindingFlags.NonPublic,null, new Type[] { typeof(string) }, null);

            return (T)ci.Invoke(new object[] {s});
        }
        else
        {
            return null;
        }
    }

    public static implicit operator string(EnumType<T> dt)
    {
        return dt?.Value;
    }


    public static bool operator ==(EnumType<T> ct1, EnumType<T> ct2)
    {
        return (string)ct1 == (string)ct2;
    }

    public static bool operator !=(EnumType<T> ct1, EnumType<T> ct2)
    {
        return !(ct1 == ct2);
    }


    public override bool Equals(object obj)
    {
        try
        {
            return (string)obj == Value;
        }
        catch
        {
            return false;
        }
    }

    public override int GetHashCode()
    {
        return Value.GetHashCode();
    }

    public static IEnumerable<T> All
     => typeof(T).GetProperties()
       .Where(p => p.PropertyType == typeof(T))
       .Select(x => (T)x.GetValue(null, null));



}

Solo allora devo dichiararlo per le mie enumerazioni:

public class TestEnum : EnumType<TestEnum> 
{

    private TestEnum(string value) : base(value)
    {}

    public static TestEnum Test1 { get { return new TestEnum("TEST1"); } }
    public static TestEnum Test2 { get { return new TestEnum("TEST2"); } }
}

Grazie per questo ottimo lavoro, stavo cercando un approccio del genere da molto tempo. Penso che dovresti ottenere 1000 punti per questo
user3492977

Oh, grazie per questo commento, e grazie per avermelo ricordato, non ho usato c # per due anni quando ho scritto questo pezzo di codice, dovrei tornare presto!
Lomithrani,

@ user3492977 ho finalmente tornato ad esso e ne ha fatto pienamente functionnal, io sono ancora in dubbio anche se si tratta di una grande idea o di una cosa inutile: D stackoverflow.com/questions/62043138/...
Lomithrani

2

La novità di .Net Core 3.0 / C # 8.0 (se l'ambiente di lavoro consente di aggiornare il progetto) è un'istruzione a commutazione breve che sembra in qualche modo enum-ish. Alla fine della giornata è la stessa vecchia noiosa dichiarazione di switch che usiamo da anni.

L'unica vera differenza qui è che l'istruzione switch ha ottenuto un nuovo seme.

public static RGBColor FromRainbow(Rainbow colorBand) =>
colorBand switch
{
    Rainbow.Red    => new RGBColor(0xFF, 0x00, 0x00),
    Rainbow.Orange => new RGBColor(0xFF, 0x7F, 0x00),
    Rainbow.Yellow => new RGBColor(0xFF, 0xFF, 0x00),
    Rainbow.Green  => new RGBColor(0x00, 0xFF, 0x00),
    Rainbow.Blue   => new RGBColor(0x00, 0x00, 0xFF),
    Rainbow.Indigo => new RGBColor(0x4B, 0x00, 0x82),
    Rainbow.Violet => new RGBColor(0x94, 0x00, 0xD3),
    _              => throw new ArgumentException(message: "invalid enum value", paramName: nameof(colorBand)),
};

Noterai che il codice sopra il quale ho copiato da qui , in realtà sta usando un enum come parametro.

Non è esattamente quello che vuoi (e fidati di me, ho voluto qualcosa di simile a quello che l'OP richiede da molto tempo), ma in realtà mi sento come se fosse un ramo di ulivo proveniente dalla SM. JMO.

Spero che aiuti qualcuno!


2

Ho usato una struttura come indicato in una risposta precedente, ma ho eliminato qualsiasi complessità. Per me, è stato quasi come creare un elenco di stringhe. Viene utilizzato nello stesso modo in cui viene utilizzata un'enumerazione.

    struct ViewTypes
    {
        public const string View1 = "Whatever string you like";
        public const string View2 = "another string";
    }

Esempio di utilizzo:

   switch( some_string_variable )
   {
      case ViewTypes.View1: /* do something */ break;
      case ViewTypes.View2: /* do something else */ break;
   }

1

Ho anche implementato alcuni enumerazioni come suggerito da @Even (via class Xe public static Xmembri), solo per scoprire in seguito che in questi giorni, a partire da .Net 4.5, c'è il diritto ToString() metodo .

Ora sto reimplementando tutto in enum.


1

Questo è un modo per usarlo come parametro fortemente tipizzato o come stringa :

public class ClassLikeEnum
{
    public string Value
    {
        get;
        private set;
    }

    ClassLikeEnum(string value) 
    {
        Value = value;
    }

    public static implicit operator string(ClassLikeEnum c)
    {
        return c.Value;
    }

    public static readonly ClassLikeEnum C1 = new ClassLikeEnum("RandomString1");
    public static readonly ClassLikeEnum C2 = new ClassLikeEnum("RandomString2");
}

1

Puoi usare due enumerazioni. Uno per il database e l'altro per la leggibilità.

Devi solo assicurarti che rimangano sincronizzati, il che sembra un piccolo costo. Non è necessario impostare i valori, basta impostare le stesse posizioni, ma l'impostazione dei valori chiarisce chiaramente che i due enum sono correlati e impedisce agli errori di riorganizzare i membri dell'enum. E un commento fa sapere al personale addetto alla manutenzione che questi sono correlati e devono essere tenuti sincronizzati.

// keep in sync with GroupTypes
public enum GroupTypeCodes
{
    OEM,
    CMB
}

// keep in sync with GroupTypesCodes
public enum GroupTypes
{
    TheGroup = GroupTypeCodes.OEM,
    TheOtherGroup = GroupTypeCodes.CMB
}

Per usarlo basta convertire prima nel codice:

GroupTypes myGroupType = GroupTypes.TheGroup;
string valueToSaveIntoDatabase = ((GroupTypeCodes)myGroupType).ToString();

Quindi, se vuoi renderlo ancora più conveniente, puoi aggiungere una funzione di estensione che funziona solo per questo tipo di enum:

public static string ToString(this GroupTypes source)
{
    return ((GroupTypeCodes)source).ToString();
}

e puoi solo fare:

GroupTypes myGroupType = GroupTypes.TheGroup;
string valueToSaveIntoDatabase = myGroupType.ToString();

Questa è una cattiva pratica: con un dipendente enumun cambiamento di valore previsto in uno potrebbe involontariamente rovinare l'altro.
Lorenz Lo Sauer,

1

In pratica stavo cercando la risposta Reflection di @ArthurC

Solo per estendere un po 'la sua risposta, puoi renderla ancora migliore avendo una funzione generica:

    // If you want for a specific Enum
    private static string EnumStringValue(GroupTypes e)
    {
        return EnumStringValue<GroupTypes>(e);
    }

    // Generic
    private static string EnumStringValue<T>(T enumInstance)
    {
        return Enum.GetName(typeof(T), enumInstance);
    } 

Quindi puoi semplicemente avvolgere qualunque cosa tu abbia

EnumStringValue(GroupTypes.TheGroup) // if you incorporate the top part

o

EnumStringValue<GroupTypes>(GroupTypes.TheGroup) // if you just use the generic

1

Tratto da @EvenMien e aggiunto in alcuni dei commenti. (Anche per il mio caso d'uso)

public struct AgentAction
{
    private AgentAction(string value) { Value = value; }

    public string Value { get; private set; }

    public override string ToString()
    {
        return this.Value;
    }

    public static AgentAction Login = new AgentAction("Logout");
    public static AgentAction Logout = new AgentAction("Logout");

    public static implicit operator string(AgentAction action) { return action.ToString(); }
}

1

Aggiungere questa classe

public class DatabasePreference {
    public DatabasePreference([CallerMemberName] string preferenceName = "") {
        PreferenceName = preferenceName;
    }
    public string PreferenceName;
}

Questo lavoro sta utilizzando CallerMemberNameper ridurre al minimo la codifica

usando:

//Declare names
public static DatabasePreference ScannerDefaultFlashLight = new DatabasePreference();
public static DatabasePreference ScannerQrCodes = new DatabasePreference();
public static DatabasePreference Scanner1dCodes = new DatabasePreference();

Provalo:

Console.WriteLine(ScannerDefaultFlashLight.PreferenceName);
Console.WriteLine(ScannerDefaultFlashLight.Scanner1dCodes);

produzione:

ScannerDefaultFlashLight
Scanner1dCodes

0

Basato su altre opinioni, questo è quello che mi viene in mente. Questo approccio evita di dover digitare .Value dove si desidera ottenere il valore costante.

Ho una classe base per tutte le enumerazioni di stringa come questa:

using System;
using Newtonsoft.Json;

[JsonConverter(typeof(ConstantConverter))]
public class StringEnum: IConvertible
{
    public string Value { get; set; }

    protected StringEnum(string value)
    {
        Value = value;
    }

    public static implicit operator string(StringEnum c)
    {
        return c.Value;
    }
    public string ToString(IFormatProvider provider)
    {
        return Value;
    }

    public TypeCode GetTypeCode()
    {
        throw new NotImplementedException();
    }

    public bool ToBoolean(IFormatProvider provider)
    {
        throw new NotImplementedException();
    }
    //The same for all the rest of IConvertible methods
}

JsonConverter è così:

using System;
using Newtonsoft.Json;

class ConstantConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return true;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        if (value == null)
        {
            serializer.Serialize(writer, null);
        }
        else
        {
            serializer.Serialize(writer, value.ToString());
        }
    }
}

E un vero enum di stringhe sarà qualcosa del genere:

public sealed class Colors : StringEnum
{
    public static Colors Red { get { return new Catalog("Red"); } }
    public static Colors Yellow { get { return new Catalog("Yellow"); } }
    public static Colors White { get { return new Catalog("White"); } }

    private Colors(string value) : base(value) { }
}

E con questo, puoi semplicemente usare Color.Red per serializzare anche su json senza usare la proprietà Value


0

Non avevo bisogno di nulla di robusto come archiviare la stringa negli attributi. Avevo solo bisogno di trasformare qualcosa del tipo MyEnum.BillEveryWeekin "fattura ogni settimana" o MyEnum.UseLegacySystemin "usare il sistema legacy" - fondamentalmente dividere l'enum con il suo involucro di cammello in indifferenti parole minuscole.

public static string UnCamelCase(this Enum input, string delimiter = " ", bool preserveCasing = false)
{
    var characters = input.ToString().Select((x, i) =>
    {

       if (i > 0 && char.IsUpper(x))
       {
           return delimiter + x.ToString(CultureInfo.InvariantCulture);
       }
       return x.ToString(CultureInfo.InvariantCulture);

    });

    var result = preserveCasing
       ? string.Concat(characters)
       : string.Concat(characters).ToLower();

    var lastComma = result.LastIndexOf(", ", StringComparison.Ordinal);

    if (lastComma > -1)
    {
       result = result.Remove(lastComma, 2).Insert(lastComma, " and ");
    }

    return result;
}

MyEnum.UseLegacySystem.UnCamelCase() output "usa il sistema legacy"

Se hai impostato più flag, lo trasformerà in un semplice inglese (delimitato da virgole tranne un "e" al posto dell'ultima virgola).

var myCustomerBehaviour = MyEnum.BillEveryWeek | MyEnum.UseLegacySystem | MyEnum.ChargeTaxes;

Console.WriteLine(myCustomerBehaviour.UnCamelCase());
//outputs "bill every week, use legacy system and charge taxes"
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.