954 Stimmen

String-Darstellung einer Enum

Ich habe die folgende Aufzählung:

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

Das Problem ist jedoch, dass ich das Wort "FORMS" benötige, wenn ich nach AuthenticationMethod.FORMS frage, und nicht die ID 1.

Ich habe die folgende Lösung für dieses Problem gefunden ( Link ):

Zunächst muss ich ein benutzerdefiniertes Attribut namens "StringValue" erstellen:

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

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

    public string Value
    {
        get { return _value; }
    }

}

Dann kann ich dieses Attribut zu meinem Enumerator hinzufügen:

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

Und natürlich brauche ich etwas, um diesen StringValue abzurufen:

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

Gut, jetzt habe ich die Werkzeuge, um einen String-Wert für einen Enumerator zu erhalten. Ich kann ihn dann wie folgt verwenden:

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

Okay, das funktioniert alles prima, aber ich finde es eine Menge Arbeit. Ich habe mich gefragt, ob es eine bessere Lösung für dieses Problem gibt.

Ich habe auch etwas mit einem Wörterbuch und statischen Eigenschaften versucht, aber auch das war nicht besser.

5 Stimmen

Auch wenn Sie das vielleicht langatmig finden, so ist es doch ein ziemlich flexibler Weg, um andere Dinge zu erledigen. Wie einer meiner Kollegen bemerkte, könnte dies in vielen Fällen verwendet werden, um Enum Helpers zu ersetzen, die Datenbankcodes auf Enum-Werte usw. abbilden.

3 Stimmen

Es handelt sich um eine "Aufzählung", nicht um einen "Enumerator".

29 Stimmen

MSDN empfiehlt, Attributklassen mit dem Suffix "Attribute" zu versehen. Also "Klasse StringValueAttribute" ;)

12voto

ILIA BROUDNO Punkte 1296

In Ihrer Frage haben Sie nie gesagt, dass Sie den numerischen Wert der Aufzählung tatsächlich irgendwo benötigen.

Wenn Sie nicht und brauchen nur ein enum des Typs string (die nicht ein Integral-Typ ist, so kann nicht eine Basis von enum sein) hier ist ein Weg:

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

können Sie die gleiche Syntax wie bei enum verwenden, um darauf zu verweisen

if (bla == AuthenticationMethod.FORMS)

Es wird etwas langsamer sein als bei numerischen Werten (Vergleich von Zeichenketten statt Zahlen), aber auf der positiven Seite ist es nicht mit Reflexion (langsam), um die Zeichenfolge zugreifen.

0 Stimmen

Wenn Sie "const" anstelle von "static readonly" verwenden, können Sie die Werte als Fallbezeichnungen in einer switch-Anweisung verwenden.

12voto

Lockszmith Punkte 1897

Aktualisierung: Wenn ich diese Seite besuche, 8 Jahre später, nachdem ich C# lange Zeit nicht mehr angefasst habe, sieht es so aus, als ob meine Antwort nicht mehr die beste Lösung ist. Ich mag wirklich die Konverter-Lösung mit Attribut-Funktionen verbunden.

Wenn Sie dies lesen, sehen Sie sich bitte auch die anderen Antworten an.
(Hinweis: Sie befinden sich oberhalb dieser Seite)


Wie die meisten von Ihnen mochte auch ich die ausgewählten Antwort von Jakub Šturc aber ich hasse es auch, Code zu kopieren und einzufügen, und versuche, dies so wenig wie möglich zu tun.

Also entschied ich mich für eine EnumBase-Klasse, von der der Großteil der Funktionalität geerbt/eingebaut wird, so dass ich mich auf den Inhalt statt auf das Verhalten konzentrieren kann.

Das Hauptproblem bei diesem Ansatz besteht darin, dass Enum-Werte zwar typsichere Instanzen sind, die Interaktion aber mit der statischen Implementierung des Enum-Klassentyps erfolgt. Mit ein wenig Hilfe von Generika-Magie habe ich nun endlich die richtige Mischung gefunden. Ich hoffe, jemand findet das genauso nützlich wie ich.

Ich beginne mit Jakubs Beispiel, aber unter Verwendung von Vererbung und Generika:

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

Und hier ist die Basisklasse:

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
}

0 Stimmen

Möglicherweise können Sie den abgeleiteten statischen Konstruktor über den statischen Basiskonstruktor aufrufen. Ich bin noch dabei, dies zu prüfen, aber bisher habe ich keine Probleme damit gefunden: stackoverflow.com/questions/55290034/

11voto

Sec Punkte 111

Wie ich dies als Erweiterungsmethode gelöst habe:

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
}

Verwendung (wobei o.OrderType eine Eigenschaft mit demselben Namen wie die Aufzählung ist):

o.OrderType.GetDescription()

Dadurch erhalte ich die Zeichenfolge "New Card" oder "Reload" anstelle der eigentlichen Aufzählungswerte NewCard und Refill.

0 Stimmen

Der Vollständigkeit halber sollten Sie eine Kopie Ihrer DescriptionAttribute-Klasse beifügen.

3 Stimmen

Bernie, DescriptionAttribute ist in System.ComponentModel

11voto

Grinn Punkte 5256

Wenn Sie hierher gekommen sind, um eine einfache "Enum" zu implementieren, deren Werte aber Strings statt Ints sind, finden Sie hier die einfachste Lösung:

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

Umsetzung:

var someStringVariable = MetricValueList.Brand;

2 Stimmen

Es ist wahrscheinlich besser, die Variablen consts zu machen, anstatt die static readonly .

3 Stimmen

Consts sind nicht gut für öffentlich zugängliche Klassen, da sie in der Kompilierzeit gebacken werden, können Sie nicht eine Drittanbieter-DLL ersetzen, ohne Ihren gesamten Code mit consts.The Leistung Offset von consts vs statische readonly ist vernachlässigbar.

10voto

Tony Basallo Punkte 2744

Ich stimme Keith zu, aber ich kann (noch) nicht für ihn stimmen.

Ich verwende eine statische Methode und eine Swith-Anweisung, um genau das zurückzugeben, was ich will. In der Datenbank speichere ich tinyint und mein Code verwendet nur die eigentliche Aufzählung, so dass die Strings für die Anforderungen der Benutzeroberfläche bestimmt sind. Nach zahlreichen Tests ergab dies die beste Leistung und die meiste Kontrolle über die Ausgabe.

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

Nach einigen Berichten führt dies jedoch zu einem möglichen Wartungsalptraum und einem gewissen Codegeruch. Ich versuche, ein Auge auf lange Enums und viele Enums zu haben, oder solche, die sich häufig ändern. Ansonsten war dies für mich eine großartige Lösung.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X