Come definire un enum con valore stringa?


97

Sto cercando di definire Enume aggiungere separatori comuni validi utilizzati in file CSV o simili. Quindi lo collegherò a ComboBoxcome origine dati, quindi ogni volta che aggiungo o rimuovo dalla definizione Enum, non avrei bisogno di cambiare nulla nella casella combinata.

Il problema è come posso definire enum con rappresentazione di stringa, qualcosa come:

public enum SeparatorChars{Comma = ",", Tab = "\t", Space = " "}


Risposte:


112

Non puoi: i valori enum devono essere valori integrali. Puoi utilizzare gli attributi per associare un valore stringa a ciascun valore enum, o in questo caso se ogni separatore è un singolo carattere puoi semplicemente usare il charvalore:

enum Separator
{
    Comma = ',',
    Tab = '\t',
    Space = ' '
}

(EDIT: solo per chiarire, non è possibile creare charil tipo sottostante dell'enumerazione, ma è possibile utilizzare le charcostanti per assegnare il valore integrale corrispondente a ciascun valore dell'enumerazione. Il tipo sottostante dell'enumerazione precedente è int.)

Quindi un metodo di estensione se ne hai bisogno:

public string ToSeparatorString(this Separator separator)
{
    // TODO: validation
    return ((char) separator).ToString();
}

Char non è valido nelle enumerazioni. Msdn: "Ogni tipo di enumerazione ha un tipo sottostante, che può essere qualsiasi tipo integrale eccetto char."
Dowhile per il

8
@dowhilefor: puoi usare un carattere letterale char per il valore , come da mia risposta. L'ho provato :)
Jon Skeet

poiché questo requisito è per i file, l'utente potrebbe aver bisogno del separatore CRLF. Funzionerà anche per quel caso?
Maheep

Grazie Jon, non conta come un carattere ?!
Saeid Yazdani

1
@ShaunLuttin: le enumerazioni sono solo "numeri con nome", quindi una stringa enum non si adatta affatto a quel modello.
Jon Skeet

82

Per quanto ne so, non ti sarà permesso di assegnare valori stringa a enum. Quello che puoi fare è creare una classe con costanti stringa al suo interno.

public static class SeparatorChars
{
    public static String Comma { get { return ",";} } 
    public static String Tab { get { return "\t,";} } 
    public static String Space { get { return " ";} } 
}

9
Il lato negativo di questo approccio opposto ad altri è che non puoi enumerarli senza fare qualcosa di extra / speciale.
caesay

Questo non aiuta ad applicare determinati valori durante il tempo di compilazione, poiché separatorora è una stringa (potrebbe essere qualsiasi cosa) invece di un Separatortipo con valori validi limitati.
ChickenFeet

71

Puoi ottenerlo ma richiederà un po 'di lavoro.

  1. Definire una classe di attributi che conterrà il valore stringa per enum.
  2. Definire un metodo di estensione che restituirà il valore dall'attributo. Ad esempio, GetStringValue (questo valore Enum) restituirà il valore dell'attributo.
  3. Quindi puoi definire l'enumerazione in questo modo ..
public enum Test: int {
    [StringValue ("a")]
    Foo = 1,
    [StringValue ("b")]
    Qualcosa = 2        
} 
  1. Per recuperare il valore da Attrinbute Test.Foo.GetStringValue ();

Fare riferimento: enumerazione con valori stringa in C #


4
Conosco questo vecchio ma è ovviamente unico e ti consente di utilizzare le enumerazioni nel codice e il valore della stringa nel DB. Incredibile
A_kat

1
Un altro commento in ritardo, ma questa è davvero una soluzione brillante
Alan

35

Per una semplice enumerazione di valori stringa (o qualsiasi altro tipo):

public static class MyEnumClass
{
    public const string 
        MyValue1 = "My value 1",
        MyValue2 = "My value 2";
}

Utilizzo: string MyValue = MyEnumClass.MyValue1;


1
Anche se questo non è un enum, penso che potrebbe fornire la migliore soluzione a ciò che l'utente sta cercando di fare. A volte, la soluzione più semplice è la migliore.
Zesty

28

Non puoi farlo con le enumerazioni, ma puoi farlo in questo modo:

public static class SeparatorChars
{
    public static string Comma = ",";

    public static string Tab = "\t";

    public static string Space = " ";
}

1
+1 Anche se penso che sia la soluzione giusta, cambierei il nome della classe o cambierei il tipo in caratteri. Giusto per essere coerenti.
Dowhile per il

Grazie, puoi dire a cosa sarà l'equivalente comboBox.DataSource = Enum.GetValues(typeof(myEnum));in questo caso?
Saeid Yazdani

1
@ Sean87: Se vuoi averlo, prenderei la risposta di JonSkeets.
Fischermaen

Penso che questa sia quasi la risposta giusta, perché non è utilizzabile all'interno dei switch-caseblocchi. I campi dovrebbero essere constin ordine. Ma ancora non può essere aiutato se lo desideri Enum.GetValues(typeof(myEnum)).
André Santaló

7
Userei constinvece di static. Le costanti sono di sola lettura e statiche e non possono essere assegnate nei costruttori (a meno che non siano campi di sola lettura).
Olivier Jacot-Descombes

12

Non puoi, perché enum può essere basato solo su un tipo numerico primitivo. Potresti provare a usare un Dictionaryinvece:

Dictionary<String, char> separators = new Dictionary<string, char>
{
    {"Comma", ','}, 
    {"Tab",  '\t'}, 
    {"Space", ' '},
};

In alternativa, puoi usare Dictionary<Separator, char>o Dictionary<Separator, string>dove Separatorè un'enumerazione normale:

enum Separator
{
    Comma,
    Tab,
    Space
}

che sarebbe un po 'più piacevole che maneggiare direttamente le corde.


11

Una classe che emula il comportamento di enum ma che usa stringinvece di intpuò essere creata come segue ...

public class GrainType
{
    private string _typeKeyWord;

    private GrainType(string typeKeyWord)
    {
        _typeKeyWord = typeKeyWord;
    }

    public override string ToString()
    {
        return _typeKeyWord;
    }

    public static GrainType Wheat = new GrainType("GT_WHEAT");
    public static GrainType Corn = new GrainType("GT_CORN");
    public static GrainType Rice = new GrainType("GT_RICE");
    public static GrainType Barley = new GrainType("GT_BARLEY");

}

Utilizzo ...

GrainType myGrain = GrainType.Wheat;

PrintGrainKeyword(myGrain);

poi...

public void PrintGrainKeyword(GrainType grain) 
{
    Console.Writeline("My Grain code is " + grain.ToString());   // Displays "My Grain code is GT_WHEAT"
}

L'unica cosa è che non puoi fare GrainType myGrain = "GT_CORN", per esempio.
colmde

potresti farlo se superassi l'operatore
SSX-SL33PY

8

È un po 'tardi per la risposta, ma forse aiuta qualcuno in futuro. Ho trovato più facile usare struct per questo tipo di problema.

Il seguente esempio è la parte copiata incollata dal codice MS:

namespace System.IdentityModel.Tokens.Jwt
{
    //
    // Summary:
    //     List of registered claims from different sources http://tools.ietf.org/html/rfc7519#section-4
    //     http://openid.net/specs/openid-connect-core-1_0.html#IDToken
    public struct JwtRegisteredClaimNames
    {
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string Actort = "actort";
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string Typ = "typ";
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string Sub = "sub";
        //
        // Summary:
        //     http://openid.net/specs/openid-connect-frontchannel-1_0.html#OPLogout
        public const string Sid = "sid";
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string Prn = "prn";
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string Nbf = "nbf";
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string Nonce = "nonce";
        //
        // Summary:
        //     http://tools.ietf.org/html/rfc7519#section-4
        public const string NameId = "nameid";

    }
}

Potresti spiegare perché questo approccio è meglio che usare una lezione?
Gerardo Grignoli

@GerardoGrignoli Non so esattamente perché usano struct invece di class in MS per questo genere di cose. Non ho nemmeno provato a scoprirlo, dato che per me funziona perfettamente. Forse prova a fare una domanda qui in pila ...
suchoss

5

Per le persone che arrivano qui alla ricerca di una risposta a una domanda più generica, è possibile estendere il concetto di classe statica se si desidera che il codice assomigli a un file enum.

Il seguente approccio funziona quando non hai finalizzato ciò enum namesche desideri e enum valuessono la stringrappresentazione di enam name; utilizzare nameof()per rendere più semplice il refactoring.

public static class Colours
{
    public static string Red => nameof(Red);
    public static string Green => nameof(Green);
    public static string Blue => nameof(Blue);
}

Questo raggiunge l'intenzione di un'enumerazione che ha valori di stringa (come il seguente pseudocodice):

public enum Colours
{
    "Red",
    "Green",
    "Blue"
}

5

Forse è troppo tardi, ma eccolo qui.

Possiamo usare l'attributo EnumMember per gestire i valori Enum.

public enum EUnitOfMeasure
{
    [EnumMember(Value = "KM")]
    Kilometer,
    [EnumMember(Value = "MI")]
    Miles
}

In questo modo il valore del risultato per EUnitOfMeasure sarà KM o MI. Questo può essere visto anche nella risposta di Andrew Whitaker .


4

Ho creato una classe base per creare enumerazioni con valori di stringa in .NET. È solo un file C # che puoi copiare e incollare nei tuoi progetti o installare tramite il pacchetto NuGet denominato StringEnum .

Utilizzo:

///<completionlist cref="HexColor"/> 
class HexColor : StringEnum<HexColor>
{
    public static readonly HexColor Blue = New("#FF0000");
    public static readonly HexColor Green = New("#00FF00");
    public static readonly HexColor Red = New("#000FF");
}

Caratteristiche

  • Il tuo StringEnum sembra in qualche modo simile a un normale enum:
    // 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)
  • Intellisense suggerirà il nome dell'enumerazione se la classe è annotata con il commento xml <completitionlist>. (Funziona sia in C # che in VB): es

Demo Intellisense

Installazione

O:

  • Installa il pacchetto NuGet StringEnum più recente , che è basato su .Net Standard 1.0quindi viene eseguito su .Net Core> = 1.0, .Net Framework> = 4.5, Mono> = 4.6, ecc.
  • Oppure incolla la seguente classe base StringEnum nel tuo progetto. ( ultima versione )
    public abstract class StringEnum<T> : IEquatable<T> where T : StringEnum<T>, new()
    {
        protected string Value;
        private static IList<T> valueList = new List<T>();
        protected static T New(string value)
        {
            if (value == null)
                return null; // the null-valued instance is null.

            var result = new T() { Value = value };
            valueList.Add(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 sensitivity.</param>
        public static T Parse(string value, bool caseSensitive = false)
        {
            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 sensitivity.</param>
        public static T TryParse(string value, bool caseSensitive = false)
        {
            if (value == null) return null;
            if (valueList.Count == 0) System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle); // force static fields initialization
            var field = valueList.FirstOrDefault(f => f.Value.Equals(value,
                    caseSensitive ? StringComparison.Ordinal
                                  : StringComparison.OrdinalIgnoreCase));
            // Not using InvariantCulture because it's only supported in NETStandard >= 2.0

            if (field == null)
                return null;

            return field;
        }
    }
  • Per il Newtonsoft.Jsonsupporto della serializzazione, copia invece questa versione estesa. StringEnum.cs

Mi sono reso conto dopo il fatto che questo codice è simile alla risposta di Ben. L'ho scritto sinceramente da zero. Tuttavia penso che abbia alcuni extra, come l' <completitionlist>hack, la classe risultante sembra più un Enum, nessun uso di riflessione su Parse (), il pacchetto NuGet e il repository dove spero di affrontare problemi e feedback in arrivo.


3

Basandosi su alcune delle risposte qui ho implementato una classe base riutilizzabile che imita il comportamento di un'enumerazione ma con stringil tipo sottostante. Supporta varie operazioni tra cui:

  1. ottenere un elenco di valori possibili
  2. conversione in stringa
  3. confronto con altri casi via .Equals, ==e!=
  4. conversione in / da JSON utilizzando un JsonConverter JSON.NET

Questa è la classe base nella sua interezza:

public abstract class StringEnumBase<T> : IEquatable<T>
    where T : StringEnumBase<T>
{
    public string Value { get; }

    protected StringEnumBase(string value) => this.Value = value;

    public override string ToString() => this.Value;

    public static List<T> AsList()
    {
        return typeof(T)
            .GetProperties(BindingFlags.Public | BindingFlags.Static)
            .Where(p => p.PropertyType == typeof(T))
            .Select(p => (T)p.GetValue(null))
            .ToList();
    }

    public static T Parse(string value)
    {
        List<T> all = AsList();

        if (!all.Any(a => a.Value == value))
            throw new InvalidOperationException($"\"{value}\" is not a valid value for the type {typeof(T).Name}");

        return all.Single(a => a.Value == value);
    }

    public bool Equals(T other)
    {
        if (other == null) return false;
        return this.Value == other?.Value;
    }

    public override bool Equals(object obj)
    {
        if (obj == null) return false;
        if (obj is T other) return this.Equals(other);
        return false;
    }

    public override int GetHashCode() => this.Value.GetHashCode();

    public static bool operator ==(StringEnumBase<T> a, StringEnumBase<T> b) => a?.Equals(b) ?? false;

    public static bool operator !=(StringEnumBase<T> a, StringEnumBase<T> b) => !(a?.Equals(b) ?? false);

    public class JsonConverter<T> : Newtonsoft.Json.JsonConverter
        where T : StringEnumBase<T>
    {
        public override bool CanRead => true;

        public override bool CanWrite => true;

        public override bool CanConvert(Type objectType) => ImplementsGeneric(objectType, typeof(StringEnumBase<>));

        private static bool ImplementsGeneric(Type type, Type generic)
        {
            while (type != null)
            {
                if (type.IsGenericType && type.GetGenericTypeDefinition() == generic)
                    return true;

                type = type.BaseType;
            }

            return false;
        }

        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            JToken item = JToken.Load(reader);
            string value = item.Value<string>();
            return StringEnumBase<T>.Parse(value);
        }

        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            if (value is StringEnumBase<T> v)
                JToken.FromObject(v.Value).WriteTo(writer);
        }
    }
}

Ed è così che implementeresti la tua "stringa enum":

[JsonConverter(typeof(JsonConverter<Colour>))]
public class Colour : StringEnumBase<Colour>
{
    private Colour(string value) : base(value) { }

    public static Colour Red => new Colour("red");
    public static Colour Green => new Colour("green");
    public static Colour Blue => new Colour("blue");
}

Che potrebbe essere usato in questo modo:

public class Foo
{
    public Colour colour { get; }

    public Foo(Colour colour) => this.colour = colour;

    public bool Bar()
    {
        if (this.colour == Colour.Red || this.colour == Colour.Blue)
            return true;
        else
            return false;
    }
}

Spero che qualcuno lo trovi utile!


2

Bene, prima provi ad assegnare le stringhe non i caratteri, anche se sono solo un carattere. usa "," invece di ",". La prossima cosa è che le enumerazioni accettano solo tipi integrali senza che chartu possa usare il valore unicode, ma ti consiglio vivamente di non farlo. Se sei certo che questi valori rimangano gli stessi, in culture e lingue diverse, utilizzerei una classe statica con stringhe const.


2

Anche se non è davvero possibile usare a charo a stringcome base per un enum, penso che questo non sia ciò che ti piace davvero fare.

Come hai detto, ti piacerebbe avere un elenco di possibilità e mostrare una rappresentazione di stringa di questo all'interno di una casella combinata. Se l'utente seleziona una di queste rappresentazioni di stringa, desideri ottenere l'enumerazione corrispondente. E questo è possibile:

Per prima cosa dobbiamo collegare una stringa a un valore enum. Questo può essere fatto usando il DescriptionAttributemodo descritto qui o qui .

Ora è necessario creare un elenco di valori enum e descrizioni corrispondenti. Questo può essere fatto utilizzando il seguente metodo:

/// <summary>
/// Creates an List with all keys and values of a given Enum class
/// </summary>
/// <typeparam name="T">Must be derived from class Enum!</typeparam>
/// <returns>A list of KeyValuePair&lt;Enum, string&gt; with all available
/// names and values of the given Enum.</returns>
public static IList<KeyValuePair<T, string>> ToList<T>() where T : struct
{
    var type = typeof(T);

    if (!type.IsEnum)
    {
        throw new ArgumentException("T must be an enum");
    }

    return (IList<KeyValuePair<T, string>>)
            Enum.GetValues(type)
                .OfType<T>()
                .Select(e =>
                {
                    var asEnum = (Enum)Convert.ChangeType(e, typeof(Enum));
                    return new KeyValuePair<T, string>(e, asEnum.Description());
                })
                .ToArray();
}

Ora avrai un elenco di coppie di valori chiave di tutte le enumerazioni e la loro descrizione. Quindi assegniamolo semplicemente come origine dati per una casella combinata.

var comboBox = new ComboBox();
comboBox.ValueMember = "Key"
comboBox.DisplayMember = "Value";
comboBox.DataSource = EnumUtilities.ToList<Separator>();

comboBox.SelectedIndexChanged += (sender, e) =>
{
    var selectedEnum = (Separator)comboBox.SelectedValue;
    MessageBox.Show(selectedEnum.ToString());
}

L'utente vede tutte le rappresentazioni di stringa dell'enumerazione e all'interno del codice otterrai il valore dell'enumerazione desiderato.


0

Non possiamo definire l'enumerazione come tipo di stringa. I tipi approvati per un enum sono byte, sbyte, short, ushort, int, uint, long o ulong.

Se hai bisogno di maggiori dettagli sull'enumerazione, segui il link sottostante, quel link ti aiuterà a capire l'enumerazione. Enumerazione

@ narendras1414


0

Per me funziona..

   public class ShapeTypes
    {
        private ShapeTypes() { }
        public static string OVAL
        {
            get
            {
                return "ov";
            }
            private set { }
        }

        public static string SQUARE
        {
            get
            {
                return "sq";
            }
            private set { }
        }

        public static string RECTANGLE
        {
            get
            {
                return "rec";
            }
            private set { }
        }
    }

0

Quello che ho recentemente iniziato a fare è usare le tuple

public static (string Fox, string Rabbit, string Horse) Animals = ("Fox", "Rabbit", "Horse");
...
public static (string Comma, string Tab, string Space) SeparatorChars = (",", "\t", " ");

-1

Enumaration Class

 public sealed class GenericDateTimeFormatType
    {

        public static readonly GenericDateTimeFormatType Format1 = new GenericDateTimeFormatType("dd-MM-YYYY");
        public static readonly GenericDateTimeFormatType Format2 = new GenericDateTimeFormatType("dd-MMM-YYYY");

        private GenericDateTimeFormatType(string Format)
        {
            _Value = Format;
        }

        public string _Value { get; private set; }
    }

Consumo enumaration

public static void Main()
{
       Country A = new Country();

       A.DefaultDateFormat = GenericDateTimeFormatType.Format1;

      Console.ReadLine();
}
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.