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

42voto

chepe Punkte 443

Verwenden Sie einfach die ToString() Methode

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

So referenzieren Sie die Zeichenfolge Tomato verwenden Sie einfach

any.Tomato.ToString();

0 Stimmen

Wow. Das war einfach. Ich weiß, der OP wollte benutzerdefinierte String-Beschreibungen hinzufügen, aber das ist, was ich brauchte. Ich hätte wissen sollen, dies zu versuchen, im Nachhinein, aber ich ging die Enum.GetName Route.

18 Stimmen

@Brent Denn meistens haben Sie die .ToString() einen anderen Wert als den von Ihnen benötigten benutzerfreundlichen Wert.

2 Stimmen

@Brent - denn das ist etwas anderes als die Frage, die gestellt wurde. Die Frage, die gestellt wird, ist, wie man diese Zeichenfolge aus einer Variablen erhält, der ein Aufzählungswert zugewiesen wurde. Dieser ist zur Laufzeit dynamisch. Dies ist die Überprüfung der Definition des Typs und zur Laufzeit festgelegt.

31voto

David C Punkte 311

Sehr einfache Lösung für dieses Problem mit .Net 4.0 und höher. Es wird kein weiterer Code benötigt.

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

Um die Zeichenfolge zu erhalten, verwenden Sie einfach:

MyStatus.Active.ToString("f");

o

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

Der Wert ist "Aktiv" oder "Archiviert".

Um die verschiedenen Zeichenkettenformate (das "f" von oben) zu sehen, wenn man Enum.ToString siehe dies Aufzählung Format Strings Seite

28voto

Ray Booysen Punkte 26896

Ich verwende das Attribut Description aus dem Namespace System.ComponentModel. Dekorieren Sie einfach das Enum und verwenden Sie dann diesen Code, um es abzurufen:

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

        }

Ein Beispiel:

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

Dieser Code eignet sich gut für Enums, für die kein "Friendly name" benötigt wird, und gibt nur den .ToString() des Enums zurück.

28voto

deadlydog Punkte 20641

Die Antwort von Jakub Šturc gefällt mir sehr gut, aber sie hat den Nachteil, dass man sie nicht mit einer switch-case-Anweisung verwenden kann. Hier ist eine leicht modifizierte Version seiner Antwort, die mit einer switch-Anweisung verwendet werden kann:

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

Sie erhalten also alle Vorteile der Antwort von Jakub Šturc und können sie zusätzlich mit einer Switch-Anweisung wie dieser verwenden:

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

0 Stimmen

Eine kürzere Lösung wäre, die Enums {} zu entfernen und stattdessen eine statische Zählung der Anzahl der konstruierten Enums zu führen. Dies hat auch den Vorteil, dass man nicht jede neue Instanz, die man erstellt, der Enum-Liste hinzufügen muss. z.B. public static int nextAvailable { get; private set; } dann im Konstruktor this.Value = nextAvailable++;

0 Stimmen

Interessante Idee @kjhf. Meine Sorge wäre allerdings, dass sich der den Enum-Werten zugewiesene Wert auch ändern könnte, wenn jemand den Code neu anordnet. Dies könnte beispielsweise dazu führen, dass der falsche Enum-Wert abgerufen wird, wenn der Enum-Wert in einer Datei/Datenbank gespeichert wird, die Reihenfolge der "new AuthenticationMethod(...)"-Zeilen geändert wird (z. B. eine entfernt wird) und dann die App erneut ausgeführt und der Enum-Wert aus der Datei/Datenbank abgerufen wird; der Enum-Wert stimmt möglicherweise nicht mit der ursprünglich gespeicherten AuthenticationMethod überein.

0 Stimmen

Guter Punkt - obwohl ich hoffe, dass sich die Leute in diesen speziellen Fällen nicht auf den Integer-Wert der Enum verlassen (oder den Enum-Code neu ordnen) -- und dieser Wert nur als Schalter und möglicherweise als Alternative zu .Equals() und .GetHashCode() verwendet wird. Wenn Sie besorgt sind, können Sie immer einen großen Kommentar mit "DO NOT REORDER" einfügen :p

13voto

Steve Mitcham Punkte 5131

Ich verwende eine Kombination aus mehreren der oben genannten Vorschläge, kombiniert mit etwas Caching. Ich habe die Idee von einem Code, den ich irgendwo im Netz gefunden habe, aber ich kann mich weder erinnern, wo ich ihn gefunden habe, noch kann ich ihn finden. Also, wenn jemand jemals etwas findet, das ähnlich aussieht, bitte kommentieren Sie mit der Zuschreibung.

Wie auch immer, die Verwendung beinhaltet die Typ-Konverter, so dass, wenn Sie die Bindung an die UI es "nur funktioniert". Sie können mit Jakub's Muster für schnelle Code-Lookup durch Initialisierung aus dem Typ-Konverter in die statischen Methoden erweitert.

Die Basisverwendung würde wie folgt aussehen

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

Es folgt der Code für den benutzerdefinierten Enum-Typ-Konverter:

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

}

0 Stimmen

Wie ist es zu verwenden? danke: MyEnum.ValueWithCustomDescription.??() oder so etwas?

0 Stimmen

Diese Antwort wurde vor etwa zehn Jahren geschrieben, so dass ich mir über den ursprünglichen Kontext nicht sicher bin. Das Problem mit SO ist, dass es schon ewig existiert. Ich glaube, dies war in Bezug auf das Hinzufügen von Daten auf UIs wie WinForms oder WPF angezeigt werden, in diesem Fall nur Bindung der Eigenschaft oder Sammlung an das UI-Steuerelement würde den Typ-Konverter direkt abholen.

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