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

887voto

Jakub Šturc Punkte 33925

トライ typensicheres-enum Muster.

public sealed class AuthenticationMethod {

    private readonly String name;
    private readonly int value;

    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){
        this.name = name;
        this.value = value;
    }

    public override String ToString(){
        return name;
    }

}

Update Die explizite (oder implizite) Typkonvertierung kann erfolgen durch

  • Hinzufügen eines statischen Feldes mit Mapping

    private static readonly Dictionary<string, AuthenticationMethod> instance = new Dictionary<string,AuthenticationMethod>();
    • n.b. Damit die Initialisierung der "enum member"-Felder beim Aufruf des Instanzkonstruktors keine NullReferenceException auslöst, stellen Sie sicher, dass das Dictionary-Feld vor den "enum member"-Feldern in Ihrer Klasse steht. Das liegt daran, dass die Initialisierer der statischen Felder in der Reihenfolge der Deklaration und vor dem statischen Konstruktor aufgerufen werden, was zu der seltsamen und notwendigen, aber verwirrenden Situation führt, dass der Instanzkonstruktor aufgerufen werden kann, bevor alle statischen Felder initialisiert wurden und bevor der statische Konstruktor aufgerufen wird.
  • Ausfüllen dieser Zuordnung im Instanzkonstruktor

    instance[name] = this;
  • und Hinzufügen benutzerdefinierter Typkonvertierungsoperator

    public static explicit operator AuthenticationMethod(string str)
    {
        AuthenticationMethod result;
        if (instance.TryGetValue(str, out result))
            return result;
        else
            throw new InvalidCastException();
    }

23 Stimmen

Es sieht aus wie eine Aufzählung, ist aber keine Aufzählung. Ich kann mir vorstellen, dass das einige interessante Probleme verursacht, wenn die Leute versuchen, AuthenticationMethods zu vergleichen. Sie müssen wahrscheinlich auch verschiedene Gleichheitsoperatoren überladen.

38 Stimmen

@Ant: Das muss ich nicht. Da wir nur eine Instanz von jeder AuthenticationMethod haben, funktioniert die von Object geerbte Referenzgleichheit gut.

0 Stimmen

+1 Ich habe diese Lösung schon einmal verwendet. Wie eine leistungsstarke Enum. Ich kann mir vorstellen, dass Sie dies nicht als Werte in einer switch-Anweisung verwenden können, oder? Das einzige Mal, dass Sie sich über das Gleichheitsproblem Sorgen machen müssen, ist, wenn dies als Struktur und nicht als Klasse deklariert wurde.

236voto

Charles Bretana Punkte 137391

Methode verwenden

Enum.GetName(Type MyEnumType,  object enumvariable)  

wie in (Angenommen Shipper ist eine definierte Enum)

Shipper x = Shipper.FederalExpress;
string s = Enum.GetName(typeof(Shipper), x);

Es gibt noch eine Reihe anderer statischer Methoden in der Klasse Enum, die es wert sind, untersucht zu werden...

6 Stimmen

Ganz genau. Ich habe ein benutzerdefiniertes Attribut für eine String-Beschreibung, aber das ist, weil ich eine benutzerfreundliche Version (mit Leerzeichen und andere Sonderzeichen), die leicht zu einer ComboBox oder so gebunden werden kann.

5 Stimmen

Enum.GetName spiegelt die Feldnamen in der Aufzählung wider - genauso wie die Funktion .ToString(). Wenn Leistung ein Thema ist, kann es ein Problem sein. Ich würde mir keine Sorgen machen, es sei denn, Sie konvertieren Lasten von Enums though.

8 Stimmen

Eine weitere Option, die Sie in Betracht ziehen können, wenn Sie eine Aufzählung mit zusätzlicher Funktionalität benötigen, ist die Verwendung einer Struktur... Sie fügen statische, schreibgeschützte benannte Eigenschaften hinzu, um die Aufzählungswerte darzustellen, die in Konstruktoren initialisiert werden, die einzelne Instanzen der Struktur erzeugen...

81voto

BenAlabaster Punkte 37617

Sie können mit ToString() auf den Namen und nicht auf den Wert verweisen.

Console.WriteLine("Auth method: {0}", AuthenticationMethod.Forms.ToString());

Die Dokumentation finden Sie hier:

http://msdn.microsoft.com/en-us/library/16c1xs4z.aspx

...und wenn Sie Ihre Enums in Pascal Case benennen (so wie ich es tue - z.B. ThisIsMyEnumValue = 1 usw.), dann können Sie einen sehr einfachen Regex verwenden, um die freundliche Form zu drucken:

static string ToFriendlyCase(this string EnumString)
{
    return Regex.Replace(EnumString, "(?!^)([A-Z])", " $1");
}

die leicht von jeder Zeichenkette aus aufgerufen werden kann:

Console.WriteLine("ConvertMyCrazyPascalCaseSentenceToFriendlyCase".ToFriendlyCase());

Ausgänge:

Meinen verrückten Pascal'schen Satz in einen freundlichen Satz umwandeln

Das erspart das Herumlaufen um die Häuser, um benutzerdefinierte Attribute zu erstellen und sie an Ihre Enums anzuhängen oder Nachschlagetabellen zu verwenden, um einen Enum-Wert mit einem freundlichen String zu verbinden. Natürlich erlaubt es Ihnen nicht, eine verschiedene freundlicherer Name als Ihre Aufzählung, die Ihre Lösung bietet.

Für komplexere Szenarien gefällt mir Ihre ursprüngliche Lösung jedoch sehr gut. Sie könnten Ihre Lösung einen Schritt weiter und machen Sie Ihre GetStringValue eine Erweiterung Methode Ihrer enum und dann würden Sie nicht brauchen, um es wie StringEnum.GetStringValue zu verweisen...

public static string GetStringValue(this AuthenticationMethod value)
{
  string output = null;
  Type type = value.GetType();
  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;
}

Sie können dann direkt von Ihrer Enum-Instanz aus darauf zugreifen:

Console.WriteLine(AuthenticationMethod.SSO.GetStringValue());

2 Stimmen

Dies ist nicht hilfreich, wenn der "freundliche Name" ein Leerzeichen benötigt. Wie zum Beispiel "Forms Authentication".

4 Stimmen

Stellen Sie also sicher, dass die Aufzählung mit Großbuchstaben wie FormsAuthentication benannt ist, und fügen Sie vor allen Großbuchstaben, die nicht am Anfang stehen, ein Leerzeichen ein. Es ist keine Raketenwissenschaft, ein Leerzeichen in eine Zeichenkette einzufügen...

4 Stimmen

Der automatische Abstand von Pascal Case-Namen wird problematisch, wenn sie Abkürzungen enthalten, die großgeschrieben werden sollten, z. B. XML oder GPS.

75voto

Keith Punkte 141163

Leider ist die Reflexion, um Attribute auf Enums zu erhalten, ziemlich langsam:

Siehe diese Frage: Kennt jemand eine schnelle Möglichkeit, um benutzerdefinierte Attribute auf einen Enum-Wert zu erhalten?

El .ToString() ist auch bei Enums recht langsam.

Sie können jedoch Erweiterungsmethoden für Enums schreiben:

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

Das ist zwar nicht toll, aber es geht schnell und erfordert keine Reflexion der Attribute oder Feldnamen.


C#6 Aktualisierung

Wenn Sie C#6 verwenden können, wird die neue nameof Operator funktioniert für Enums, also nameof(MyEnum.WINDOWSAUTHENTICATION) wird umgewandelt in "WINDOWSAUTHENTICATION" unter Kompilierzeit und ist damit der schnellste Weg, um Enum-Namen zu erhalten.

Beachten Sie, dass dies die explizite Enum in eine Inline-Konstante umwandelt, so dass es nicht für Enums funktioniert, die Sie in einer Variablen haben. Also:

nameof(AuthenticationMethod.FORMS) == "FORMS"

Aber...

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

24 Stimmen

Sie können die Attributwerte einmal abrufen und sie in ein Dictionary<MyEnum,string> stellen, um den deklarativen Aspekt zu erhalten.

1 Stimmen

Ja, das haben wir in einer Anwendung mit vielen Enums gemacht, als wir herausfanden, dass die Reflexion der Flaschenhals war.

0 Stimmen

Danke Jon und Keith, ich habe euren Vorschlag für das Wörterbuch übernommen. Funktioniert prima (und schnell!).

61voto

Mangesh Pimpalkar Punkte 3959

Ich verwende eine Erweiterungsmethode:

public static class AttributesHelperExtension
    {
        public static string ToDescription(this Enum value)
        {
            var da = (DescriptionAttribute[])(value.GetType().GetField(value.ToString())).GetCustomAttributes(typeof(DescriptionAttribute), false);
            return da.Length > 0 ? da[0].Description : value.ToString();
        }
}

Dekorieren Sie nun die enum mit:

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

Wenn Sie anrufen

AuthenticationMethod.FORMS.ToDescription() erhalten Sie "FORMS" .

1 Stimmen

Ich musste hinzufügen using System.ComponentModel; Außerdem funktioniert diese Methode nur, wenn der String-Wert mit dem Namen des Enums übereinstimmen soll. OP wollte einen anderen Wert.

3 Stimmen

Meinst du nicht, wenn du anrufst AuthenticationMethod.FORMS.ToDescription() ?

0 Stimmen

Diese Lösung funktioniert hervorragend und ermöglicht es, dass sich die Beschreibung vom Namen der Enum-Variable unterscheidet.

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