Enum ToString mit benutzerfreundlichen Zeichenfolgen

282

Meine Aufzählung besteht aus folgenden Werten:

private enum PublishStatusses{
    NotCompleted,
    Completed,
    Error
};

Ich möchte diese Werte jedoch benutzerfreundlich ausgeben können.
Ich muss nicht wieder von String zu Wert wechseln können.

Boris Callens
quelle
Mögliches Duplikat von C # String-Aufzählungen
nawfal
Mögliches Duplikat der String-Darstellung einer Enum
Liam

Antworten:

350

Ich verwende das DescriptionAttribut aus dem System.ComponentModel-Namespace. Dekorieren Sie einfach die Aufzählung:

private enum PublishStatusValue
{
    [Description("Not Completed")]
    NotCompleted,
    Completed,
    Error
};

Verwenden Sie dann diesen Code, um ihn abzurufen:

public static string GetDescription<T>(this T 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();
}
Ray Booysen
quelle
12
Dieses Beispiel ist leichter zu lesen. stackoverflow.com/questions/1415140/…
RayLoveless
31
Ich vermute, dass die Verwendung der in dieser Lösung beschriebenen Reflexion einen erheblichen Leistungseinbruch aufweist. Der Code für Wills Methode zur Verwendung der ToFriendlyString-Erweiterungsmethode ist viel einfacher zu verstehen, und seine Leistung sollte auch extrem schnell sein.
Humbads
1
Ich mag die Version, die @RayL verlinkt hat, da sie nur die Erweiterungsmethode zu Enums hinzufügt. Wenn das alles ist, ArgumentException
wofür
4
Dies bedeutet, dass jede Aufzählung eine eigene Erweiterungsmethode benötigt. Dies ist allgemeiner und erfordert mehr Arbeit, aber Sie möchten wahrscheinlich quantifizieren, was "schnell" bedeutet, bevor wir uns für die Leistung entscheiden.
Ray Booysen
2
@petar, das funktioniert, aber nicht, wenn den Benutzern benutzerfreundliche Zeichenfolgen angezeigt werden sollen. MY_TYPE hat den Unterstrich und ist nicht anpassbar.
Ray Booysen
354

Ich mache das mit Erweiterungsmethoden:

public enum ErrorLevel
{
  None,
  Low,
  High,
  SoylentGreen
}

public static class ErrorLevelExtensions
{
  public static string ToFriendlyString(this ErrorLevel me)
  {
    switch(me)
    {
      case ErrorLevel.None:
        return "Everything is OK";
      case ErrorLevel.Low:
        return "SNAFU, if you know what I mean.";
      case ErrorLevel.High:
        return "Reaching TARFU levels";
      case ErrorLevel.SoylentGreen:
        return "ITS PEOPLE!!!!";
      default:
        return "Get your damn dirty hands off me you FILTHY APE!";
    }
  }
}

quelle
6
Dies ist so viel sauberer als die Attributantwort. Nett!
Pennyrave
3
@ Pennyrave: Eh. Viele UI-Komponenten erwarten, DisplayNameAttribute und DescriptionAttribute zu finden und zu verwenden. Tatsächlich verwende ich jetzt diese und eine Erweiterungsmethode, um diese Werte einfach herauszuholen.
60
Das Problem, das ich dabei sehe, ist, dass Sie diese Erweiterungsmethoden ständig schreiben. Mit dem Attributmechanismus ist es eine einfache Möglichkeit, ihn zu dekorieren und immer nur eine Methode aufzurufen.
Ray Booysen
5
Nicht sicher was du meinst?
Ray Booysen
9
Es ist besser, meiner Meinung nach , die erlauben defaultFall Implementierung zurückzukehren me.ToString()Schaltergehäuse - Anweisungen für die ENUM - Werte und nur zur Verfügung stellen , dass Sie außer Kraft setzen möchten. In Ihrem Beispiel habe ich festgestellt, dass sie alle unterschiedlich sind, aber in tatsächlichen Anwendungsfällen vermute ich, dass die meisten Einzelwort-Aufzählungswerte ausreichen und Sie nur Überschreibungen für Mehrwort-Aufzählungswerte bereitstellen.
Scott
78

Vielleicht fehlt mir etwas, aber was ist mit Enum.GetName los?

public string GetName(PublishStatusses value)
{
    return Enum.GetName(typeof(PublishStatusses), value)
}

Bearbeiten: Für benutzerfreundliche Zeichenfolgen müssen Sie eine .ressource durchlaufen, um die Internationalisierung / Lokalisierung durchzuführen. Es ist wahrscheinlich besser, einen festen Schlüssel zu verwenden, der auf dem Enum-Schlüssel basiert, als ein Dekorator-Attribut.

Annakata
quelle
11
Ich gebe den Literalwert der Aufzählung zurück, nicht einen benutzerfreundlichen.
Boris Callens
2
oic - nun, es gibt einen ziemlich großen Fall, dass Sie dann eine String-Ressourcenbibliothek basierend auf diesem Wert durchgehen müssen, weil die Alternative (Dekorator-Attribute) I18N nicht unterstützt
annakata
1
Im Fall von I18N würde ich die GetDescription () -Methode in der Ressourcenbibliothek nach einer übersetzten Zeichenfolge durchsuchen und auf die Beschreibung zurückgreifen und dann auf das Literal zurückgreifen.
Boris Callens
3
+1 für MyEnum.ToString () als Ressourcenschlüssel für die Lokalisierung. Ich mache das seit Jahren
Jackvsworld
1
@annakata Wir haben den Attributmechanismus tatsächlich um die Unterstützung von l18N erweitert. Dies ist eine einfache Änderung.
Ray Booysen
23

Ich habe eine umgekehrte Erweiterungsmethode erstellt, um die Beschreibung wieder in einen Aufzählungswert umzuwandeln:

public static T ToEnumValue<T>(this string enumerationDescription) where T : struct
{
    var type = typeof(T);

    if (!type.IsEnum)
        throw new ArgumentException("ToEnumValue<T>(): Must be of enum type", "T");

    foreach (object val in System.Enum.GetValues(type))
        if (val.GetDescription<T>() == enumerationDescription)
            return (T)val;

    throw new ArgumentException("ToEnumValue<T>(): Invalid description for enum " + type.Name, "enumerationDescription");
}
Brian Richardson
quelle
15
Es tut mir leid, aber danke, dass Sie versucht haben, hilfreich zu sein! Da es sich um eine Q & A-Site handelt, sollten Antworten ein Versuch sein, die Frage direkt zu beantworten. In der Frage heißt es ausdrücklich: „ Ich muss nicht wieder von String zu Wert wechseln können. “ Nochmals vielen Dank!
Jesse
8
Danke für die positive Kritik. Es ist immer schwierig, neu in einer Site zu sein und etwas über ihre Kultur und Nuancen zu lernen. Ich bin froh, dass es Leute wie Sie gibt, die die neuen Leute klarstellen. Nochmals vielen Dank, dass Sie sich nicht auf den neuen Typen eingelassen haben.
Brian Richardson
6
@ Jesse Und 4 Jahre später freut sich jemand, bjrichardson Code hier zu finden! SO könnte eine Q & A-Site sein, aber das bedeutet nicht, dass die Fragen nach der Beantwortung tot sind.
John
18

Die einfachste Lösung besteht darin, eine benutzerdefinierte Erweiterungsmethode zu verwenden (zumindest in .NET 3.5 - Sie können sie einfach in eine statische Hilfsmethode für frühere Framework-Versionen konvertieren).

public static string ToCustomString(this PublishStatusses value)
{
    switch(value)
    {
        // Return string depending on value.
    }
    return null;
}

Ich gehe hier davon aus, dass Sie etwas anderes als den tatsächlichen Namen des Aufzählungswerts zurückgeben möchten (den Sie durch einfaches Aufrufen von ToString erhalten können).

Noldorin
quelle
Obwohl gültig, mag ich das Attribut viel mehr. Auf diese Weise kann ich meine toSTring-Methode in eine separate Bibliothek stellen, während ich die benutzerdefinierte Zeichenfolgendarstellung mit der Aufzählung selbst platziere
Boris Callens
1
Meinetwegen. Ich nehme an, ein Vorteil dieser Methode besteht darin, dass Sie der Methode, die eine Statusvariable angibt, ein Argument hinzufügen und dann ändern können, welche Zeichenfolgendarstellung abhängig davon zurückgegeben wird.
Noldorin
1
Ja, es hängt alles vom Umfang der Methode ab, denke ich. Während die Attributmethode allgemeiner ist, ist Ihre Lösung lokaler. Am Ende geht es nur um die Anforderungen.
Boris Callens
1
Sie können Erweiterungsmethoden beliebig platzieren. Sie müssen nur darauf verweisen, wo Sie sie verwenden möchten.
Ja, aber dies würde bedeuten, dass diese eine Erweiterungsmethode jedes Mal neu geschrieben werden sollte, wenn Sie eine neue Aufzählung einführen, für die Sie einen Anzeigenamen haben möchten. Dies würde auch bedeuten, dass ALLE Ihre Anwendungen Anzeigenamen für ALLE Ihre anderen Anwendungen enthalten würden ...
Boris Callens
13

Dieser andere Beitrag ist Java. Sie können keine Methoden in Enums in C # einfügen.

mach einfach so etwas:

PublishStatusses status = ...
String s = status.ToString();

Wenn Sie unterschiedliche Anzeigewerte für Ihre Aufzählungswerte verwenden möchten, können Sie Attribute und Reflexion verwenden.

Lemmy
quelle
3
toString ist nicht sicher , in allen Fällen - eine Enumeration mit mehreren Einträgen mit dem gleichen Wert (zB für integer Aufzählungen) den Schlüssel des ersten Anpassungswertes zurückgeben, nicht der Schlüssel des Elements getestet, deshalb Enum.GetName bevorzugt wird
Annakata
4
Nun, es war die einfachste Lösung für seine spezifische Aufzählung
Lemmy
9

Am einfachsten ist es, diese Erweiterungsklasse in Ihr Projekt aufzunehmen. Sie funktioniert mit jeder Aufzählung im Projekt:

public static class EnumExtensions
{
    public static string ToFriendlyString(this Enum code)
    {
        return Enum.GetName(code.GetType(), code);
    }
}

Verwendungszweck:

enum ExampleEnum
{
    Demo = 0,
    Test = 1, 
    Live = 2
}

...

ExampleEnum ee = ExampleEnum.Live;
Console.WriteLine(ee.ToFriendlyString());
Milan Švec
quelle
2
Es ist ein Rätsel, warum dieser Kommentar nicht akzeptiert oder am meisten positiv bewertet wird - keine Reflexion, keine unnötigen Attribute, ideal für einfache Situationen, in denen die Aufzählung bereits gut benannt ist. Sie können diese Antwort noch einen Schritt weiter gehen und Leerzeichen zwischen Großbuchstaben einfügen, bevor Sie "Meine Aufzählung" zurückgeben.
Vix
12
Wenn die Aufzählung bereits gut benannt ist, ist keine Erweiterungsmethode erforderlich. Verwenden Sie einfach die vorhandene ToString () -Methode. string result = "Result: " + ee;
John
Dies sollte die beste Antwort sein. Es funktioniert für jede Aufzählung. Sie können es sogar mit einer bestimmten Aufzählung implementieren, indem Sie den Aufzählungstyp des Parameters in die tatsächliche Aufzählung ändern, für die es verwendet werden soll.
Juanu Haedo
6
Diese Antwort und alle Kommentare ignorieren die ursprüngliche Anforderung einer erweiterten Beschreibung. Ihr habt die Übung, etwas anderes als den Standard-ToString-Wert zurückzugeben, total verpasst. Ich werde hier nicht alle Notizen für diese Antwort ablehnen, aber ich möchte es auf jeden Fall.
TonyG
8

Einige andere primitivere Optionen, die Klassen / Referenztypen vermeiden:

  • Array-Methode
  • Verschachtelte Strukturmethode

Array-Methode

private struct PublishStatusses
{
    public static string[] Desc = {
        "Not Completed",
        "Completed",
        "Error"
    };

    public enum Id
    {
        NotCompleted = 0,
        Completed,
        Error
    };
}

Verwendung

string desc = PublishStatusses.Desc[(int)PublishStatusses.Id.Completed];

Verschachtelte Strukturmethode

private struct PublishStatusses
{
    public struct NotCompleted
    {
        public const int Id = 0;
        public const string Desc = "Not Completed";
    }

    public struct Completed
    {
        public const int Id = 1;
        public const string Desc = "Completed";
    }

    public struct Error
    {
        public const int Id = 2;
        public const string Desc = "Error";
    }            
}

Verwendung

int id = PublishStatusses.NotCompleted.Id;
string desc = PublishStatusses.NotCompleted.Desc;

Update (03/09/2018)

Eine Mischung aus Erweiterungsmethoden und der ersten Technik oben.

Ich bevorzuge es, Enums dort zu definieren, wo sie "hingehören" (am nächsten an ihrer Herkunftsquelle und nicht in einem gemeinsamen globalen Namespace).

namespace ViewModels
{
    public class RecordVM
    {
        //public enum Enum { Minutes, Hours }
        public struct Enum
        {
            public enum Id { Minutes, Hours }
            public static string[] Name = { "Minute(s)", "Hour(s)" };
        }
    }
}

Die Erweiterungsmethode scheint für einen gemeinsamen Bereich geeignet zu sein, und die "lokalisierte" Definition der Aufzählung macht die Erweiterungsmethode jetzt ausführlicher.

namespace Common
{
    public static class EnumExtensions
    {
        public static string Name(this RecordVM.Enum.Id id)
        {
            return RecordVM.Enum.Name[(int)id];
        }
    }   
}

Ein Anwendungsbeispiel für die Aufzählung und ihre Erweiterungsmethode.

namespace Views
{
    public class RecordView 
    {
        private RecordDataFieldList<string, string> _fieldUnit;

        public RecordView()
        {
            _fieldUnit.List = new IdValueList<string, string>
            {            
                new ListItem<string>((int)RecordVM.Enum.Id.Minutes, RecordVM.Enum.Id.Minutes.Name()),
                new ListItem<string>((int)RecordVM.Enum.Id.Hours, RecordVM.Enum.Id.Hours.Name())
            };
        }

        private void Update()
        {    
            RecordVM.Enum.Id eId = DetermineUnit();

            _fieldUnit.Input.Text = _fieldUnit.List.SetSelected((int)eId).Value;
        }
    }
}

Hinweis: Ich habe mich tatsächlich dazu entschlossen, den EnumWrapper (und das NameArray) zu entfernen, da es am besten ist, dass die Namenszeichenfolgen aus einer Ressource (z. B. Konfigurationsdatei oder Datenbank) stammen, anstatt fest codiert zu sein, und weil ich die Erweiterungsmethode in das Feld eingefügt habe ViewModelsNamespace (nur in einer anderen "CommonVM.cs" -Datei). Außerdem wird das Ganze .Idablenkend und umständlich.

namespace ViewModels
{
    public class RecordVM
    {
        public enum Enum { Minutes, Hours }
        //public struct Enum
        //{
        //    public enum Id { Minutes, Hours }
        //    public static string[] Name = { "Minute(s)", "Hour(s)" };
        //}
    }
}

CommonVM.cs

//namespace Common
namespace ViewModels
{
    public static class EnumExtensions
    {
        public static string Name(this RecordVM.Enum id)
        {
            //return RecordVM.Enum.Name[(int)id];
            switch (id)
            {
                case RecordVM.Enum.Minutes: return "Minute(s)";                    
                case RecordVM.Enum.Hours: return "Hour(s)";
                default: return null;
            }
        }
    }   
}

Ein Anwendungsbeispiel für die Aufzählung und ihre Erweiterungsmethode.

namespace Views
{
    public class RecordView 
    {
        private RecordDataFieldList<string, string> _fieldUnit

        public RecordView()
        {
            _fieldUnit.List = new IdValueList<string, string>
            {            
                new ListItem<string>((int)RecordVM.Enum.Id.Minutes, RecordVM.Enum.Id.Minutes.Name()),
                new ListItem<string>((int)RecordVM.Enum.Id.Hours, RecordVM.Enum.Id.Hours.Name())
            };
        }

        private void Update()
        {    
            RecordVM.Enum eId = DetermineUnit();

            _fieldUnit.Input.Text = _fieldUnit.List.SetSelected((int)eId).Value;
        }
    }
}
Sam ist
quelle
+ 1-1 = 0 Stimme: Diese Lösung behält die Enum-Syntax bei und löst das Problem elegant ohne Reflexion oder komplexen Code, also +1 dort. Aber es verliert Funktionen von Enums selbst. Während IMO dies eine gute Option ist, beantwortet es nicht die eigentliche Frage und erhält eine -1. Net 0. Leider haben wir keine Möglichkeit, dies in SO besser aufzuzeichnen.
TonyG
@ TonyG Fair genug. Nachdem ich einige Fragen zur Bewertung der .net-Fähigkeiten von pluarlsight.com verpasst hatte, wurde mir klar, wie detailliert die C # -Enum-Werte sind. Daher ist es wahrscheinlich eine gute Idee, zumindest ihre Fähigkeiten zu kennen, bevor ich mich für eine Methode entscheide (insbesondere für die allgegenwärtige Verwendung und das Refactoring) kann ein bisschen Zeit sein;).
Samis
7

Sie können das Humanizer- Paket mit der Möglichkeit Humanize Enums verwenden. Ein Beispiel:

enum PublishStatusses
{
    [Description("Custom description")]
    NotCompleted,
    AlmostCompleted,
    Error
};

Dann können Sie die HumanizeErweiterungsmethode für enum direkt verwenden:

var st1 = PublishStatusses.NotCompleted;
var str1 = st1.Humanize(); // will result in Custom description

var st2 = PublishStatusses.AlmostCompleted;
var str2 = st2.Humanize(); // will result in Almost completed (calculated automaticaly)
Konrad Kokosa
quelle
Es verwendet auch Reflexion und wird nicht zwischengespeichert. github.com/Humanizr/Humanizer/blob/…
Konrad
Es wird so langsam sein wie die Lösung in der ersten Antwort von Ray
Konrad
5

In Bezug auf Ray Booysen gibt es einen Fehler im Code: Enum ToString mit benutzerfreundlichen Zeichenfolgen

Sie müssen mehrere Attribute für die Aufzählungswerte berücksichtigen.

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 && attrs.Where(t => t.GetType() == typeof(DescriptionAttribute)).FirstOrDefault() != null)
            {
                //Pull out the description value
                return ((DescriptionAttribute)attrs.Where(t=>t.GetType() == typeof(DescriptionAttribute)).FirstOrDefault()).Description;
            }
        }
        //If we have no description attribute, just return the ToString of the enum
        return enumerationValue.ToString();
Joel MC
quelle
4
Das Weglassen einer Prüfung für mehrere Beschreibungsattribute ist absichtlich. Wenn die Aufzählung zwei enthält und Sie eine Beschreibung erstellen, würde ich gerne glauben, dass dies ein außergewöhnlicher Umstand ist. Ich denke, der eigentliche Fehler ist, dass ich kein Single () mache, um eine Ausnahme auszulösen. Ansonsten macht die gesamte Methodensignatur keinen Sinn. GetDescription ()? Welche Beschreibung? Ein Aggregat?
Ray Booysen
4
public enum MyEnum
{
    [Description("Option One")]
    Option_One
}

public static string ToDescriptionString(this Enum This)
{
    Type type = This.GetType();

    string name = Enum.GetName(type, This);

    MemberInfo member = type.GetMembers()
        .Where(w => w.Name == name)
        .FirstOrDefault();

    DescriptionAttribute attribute = member != null
        ? member.GetCustomAttributes(true)
            .Where(w => w.GetType() == typeof(DescriptionAttribute))
            .FirstOrDefault() as DescriptionAttribute
        : null;

    return attribute != null ? attribute.Description : name;
}
Diogo Freitas
quelle
3
Es ist immer schön, einen Text zu schreiben, der erklärt, warum dies funktionieren sollte und warum die OPs dies nicht waren.
Phaberest
Nur zu Ihrer Information, C # -Code-Konventionen möchten lokale Variablen und Methodenparameter mit Kleinbuchstaben. Eine Ausnahme bildet der thisParameter in Erweiterungsmethoden, der Thisin vielen Beispielen im Web aufgerufen wird . Wenn Sie es wie Ihren Typ aufrufen ( Enum Enum), ist der Code weniger lesbar.
Massimiliano Kraus
4

Verwenden Sie anstelle einer Aufzählung eine statische Klasse.

ersetzen

private enum PublishStatuses{
    NotCompleted,
    Completed,
    Error
};

mit

private static class PublishStatuses{
    public static readonly string NotCompleted = "Not Completed";
    public static readonly string Completed = "Completed";
    public static readonly string Error = "Error";
};

es wird so verwendet

PublishStatuses.NotCompleted; // "Not Completed"

Problem mit den besten "Erweiterungsmethoden" -Lösungen:

Eine private Aufzählung wird häufig in einer anderen Klasse verwendet. Die Erweiterungsmethodenlösung ist dort nicht gültig, da sie in einer eigenen Klasse sein muss. Diese Lösung kann privat und in eine andere Klasse eingebettet sein.

Asher Girlande
quelle
2

Ich bin zufällig ein VB.NET-Fan. Hier ist meine Version, in der die DescriptionAttribute-Methode mit einer Erweiterungsmethode kombiniert wird. Zunächst die Ergebnisse:

Imports System.ComponentModel ' For <Description>

Module Module1
  ''' <summary>
  ''' An Enum type with three values and descriptions
  ''' </summary>
  Public Enum EnumType
    <Description("One")>
    V1 = 1

    ' This one has no description
    V2 = 2

    <Description("Three")>
    V3 = 3
  End Enum

  Sub Main()
    ' Description method is an extension in EnumExtensions
    For Each v As EnumType In [Enum].GetValues(GetType(EnumType))
      Console.WriteLine("Enum {0} has value {1} and description {2}",
        v,
        CInt(v),
        v.Description
      )
    Next
    ' Output:
    ' Enum V1 has value 1 and description One
    ' Enum V2 has value 2 and description V2
    ' Enum V3 has value 3 and description Three
  End Sub
End Module

Grundlegendes: eine Aufzählung namens EnumType mit drei Werten V1, V2 und V3. Die "Magie" geschieht im Console.WriteLine-Aufruf in Sub Main (), wo das letzte Argument einfach ist v.Description. Dies gibt "Eins" für V1, "V2" für V2 und "Drei" für V3 zurück. Diese Beschreibungsmethode ist tatsächlich eine Erweiterungsmethode, die in einem anderen Modul namens EnumExtensions definiert ist:

Option Strict On
Option Explicit On
Option Infer Off

Imports System.Runtime.CompilerServices
Imports System.Reflection
Imports System.ComponentModel

Module EnumExtensions
  Private _Descriptions As New Dictionary(Of String, String)

  ''' <summary>
  ''' This extension method adds a Description method
  ''' to all enum members. The result of the method is the
  ''' value of the Description attribute if present, else
  ''' the normal ToString() representation of the enum value.
  ''' </summary>
  <Extension>
  Public Function Description(e As [Enum]) As String
    ' Get the type of the enum
    Dim enumType As Type = e.GetType()
    ' Get the name of the enum value
    Dim name As String = e.ToString()

    ' Construct a full name for this enum value
    Dim fullName As String = enumType.FullName + "." + name

    ' See if we have looked it up earlier
    Dim enumDescription As String = Nothing
    If _Descriptions.TryGetValue(fullName, enumDescription) Then
      ' Yes we have - return previous value
      Return enumDescription
    End If

    ' Find the value of the Description attribute on this enum value
    Dim members As MemberInfo() = enumType.GetMember(name)
    If members IsNot Nothing AndAlso members.Length > 0 Then
      Dim descriptions() As Object = members(0).GetCustomAttributes(GetType(DescriptionAttribute), False)
      If descriptions IsNot Nothing AndAlso descriptions.Length > 0 Then
        ' Set name to description found
        name = DirectCast(descriptions(0), DescriptionAttribute).Description
      End If
    End If

    ' Save the name in the dictionary:
    _Descriptions.Add(fullName, name)

    ' Return the name
    Return name
  End Function
End Module

Da das Nachschlagen von Beschreibungsattributen mit Reflectionlangsam ist, werden die Suchvorgänge auch privat zwischengespeichertDictionary , der bei Bedarf ausgefüllt wird.

(Entschuldigung für die VB.NET-Lösung - die Übersetzung in C # sollte relativ einfach sein, und mein C # ist bei neuen Themen wie Erweiterungen verrostet.)

MarkusT
quelle
2

Saubere Zusammenfassung der obigen Vorschläge mit Beispiel:

namespace EnumExtensions {

using System;
using System.Reflection;

public class TextAttribute : Attribute {
   public string Text;
   public TextAttribute( string text ) {
      Text = text;
   }//ctor
}// class TextAttribute

public static class EnumExtender {

public static string ToText( this Enum enumeration ) {

   MemberInfo[] memberInfo = enumeration.GetType().GetMember( enumeration.ToString() );

   if ( memberInfo != null && memberInfo.Length > 0 ) {

      object[] attributes = memberInfo[ 0 ].GetCustomAttributes( typeof(TextAttribute),  false );

      if ( attributes != null && attributes.Length > 0 ) {
         return ( (TextAttribute)attributes[ 0 ] ).Text;
      }

   }//if

   return enumeration.ToString();

}//ToText

}//class EnumExtender

}//namespace

VERWENDUNG:

using System;
using EnumExtensions;

class Program {

public enum Appearance {

  [Text( "left-handed" ) ]
  Left,

  [Text( "right-handed" ) ]
  Right,

}//enum

static void Main( string[] args ) {

   var appearance = Appearance.Left;
   Console.WriteLine( appearance.ToText() );

}//Main

}//class
unterstreichen
quelle
1
In C # gibt es ein Attribut [Description ("")]. Warum nicht dieses verwenden?
Stefan Koenen
Natürlich ist die Verwendung von [Beschreibung ("")] ein guter Weg. Aber ich wollte, dass die Probe vollständig ist.
unterstreichen
2

Verwenden Sie Enum.GetName

Über den obigen Link ...

using System;

public class GetNameTest {
    enum Colors { Red, Green, Blue, Yellow };
    enum Styles { Plaid, Striped, Tartan, Corduroy };

    public static void Main() {

        Console.WriteLine("The 4th value of the Colors Enum is {0}", Enum.GetName(typeof(Colors), 3));
        Console.WriteLine("The 4th value of the Styles Enum is {0}", Enum.GetName(typeof(Styles), 3));
    }
}
// The example displays the following output:
//       The 4th value of the Colors Enum is Yellow
//       The 4th value of the Styles Enum is Corduroy
JMH
quelle
2

Gemäß dieser Dokumentation: https://docs.microsoft.com/pt-br/dotnet/api/system.enum.tostring?view=netframework-4.8

Es ist möglich, einen Enumerator einfach in ein Format wie das folgende zu konvertieren:

public enum Example
{
    Example1,
    Example2
}

Console.WriteLine(Example.Example1.ToString("g"));

//Outputs: "Example1"

Sie können alle möglichen Formate unter diesem Link sehen: https://docs.microsoft.com/pt-br/dotnet/api/system.string?view=netframework-4.8

Emanuel Hiroshi
quelle
1

Dies ist ein Update von Ray Booysens Code, der die generische GetCustomAttributes-Methode und LINQ verwendet, um die Dinge etwas aufgeräumter zu machen.

    /// <summary>
    /// Gets the value of the <see cref="T:System.ComponentModel.DescriptionAttribute"/> on an struct, including enums.  
    /// </summary>
    /// <typeparam name="T">The type of the struct.</typeparam>
    /// <param name="enumerationValue">A value of type <see cref="T:System.Enum"/></param>
    /// <returns>If the struct has a Description attribute, this method returns the description.  Otherwise it just calls ToString() on the struct.</returns>
    /// <remarks>Based on http://stackoverflow.com/questions/479410/enum-tostring/479417#479417, but useful for any struct.</remarks>
    public static string GetDescription<T>(this T enumerationValue) where T : struct
    {
        return enumerationValue.GetType().GetMember(enumerationValue.ToString())
                .SelectMany(mi => mi.GetCustomAttributes<DescriptionAttribute>(false),
                    (mi, ca) => ca.Description)
                .FirstOrDefault() ?? enumerationValue.ToString();
    }   
Richard Anthony Hein
quelle
Sie wissen nicht, warum es generisch sein muss? Wenn Sie Reflexion verwenden wollen?
Lee Louviere
@LeeLouviere Hauptsächlich, um das Boxen zu vermeiden, wenn die Struktur (Werttyp) als Parameter übergeben wird.
Richard Anthony Hein
1
Verwenden Sie stattdessen numerationValue.GetType (): typeof (T).
Slava
1
Riesige einzeilige Verbesserung gegenüber akzeptierten Antworten, ohne dass (YMMV) die Lesbarkeit verliert. Ja, mit Typ (T).
TonyG
1

Noch sauberere Zusammenfassung:

using System;
using System.Reflection;

public class TextAttribute : Attribute
{
    public string Text;
    public TextAttribute(string text)
    {
        Text = text;
    }
}  

public static class EnumExtender
{
    public static string ToText(this Enum enumeration)
    {
        var memberInfo = enumeration.GetType().GetMember(enumeration.ToString());
        if (memberInfo.Length <= 0) return enumeration.ToString();

        var attributes = memberInfo[0].GetCustomAttributes(typeof(TextAttribute), false);
        return attributes.Length > 0 ? ((TextAttribute)attributes[0]).Text : enumeration.ToString();
    }
}

Gleiche Verwendung wie der Unterstrich beschreibt.

StefanDK
quelle
0

Für Flaggen Aufzählung einschließlich.

    public static string Description(this Enum value)
    {
        Type type = value.GetType();

        List<string> res = new List<string>();
        var arrValue = value.ToString().Split(',').Select(v=>v.Trim());
        foreach (string strValue in arrValue)
        {
            MemberInfo[] memberInfo = type.GetMember(strValue);
            if (memberInfo != null && memberInfo.Length > 0)
            {
                object[] attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);

                if (attrs != null && attrs.Length > 0 && attrs.Where(t => t.GetType() == typeof(DescriptionAttribute)).FirstOrDefault() != null)
                {
                    res.Add(((DescriptionAttribute)attrs.Where(t => t.GetType() == typeof(DescriptionAttribute)).FirstOrDefault()).Description);
                }
                else
                    res.Add(strValue);
            }
            else
                res.Add(strValue);
        }

        return res.Aggregate((s,v)=>s+", "+v);
    }
Lukich
quelle
0

Wenn Sie nur ein Leerzeichen zwischen den Wörtern einfügen möchten, ist dies so einfach wie

string res = Regex.Replace(PublishStatusses.NotCompleted, "[A-Z]", " $0").Trim();
VladL
quelle
0

Ich verwende eine generische Klasse zum Speichern der Enum / Description-Paare und eine verschachtelte Hilfsklasse zum Abrufen der Beschreibung.

Die Aufzählung :

enum Status { Success, Fail, Pending }

Die generische Klasse:

Hinweis: Da eine generische Klasse nicht durch eine Aufzählung eingeschränkt werden kann, beschränke ich sie stattdessen auf struct und suche im Konstruktor nach Aufzählung .

public class EnumX<T> where T : struct
{
    public T Code { get; set; }
    public string Description { get; set; }

    public EnumX(T code, string desc)
    {
        if (!typeof(T).IsEnum) throw new NotImplementedException();

        Code = code;
        Description = desc;
    }

    public class Helper
    {
        private List<EnumX<T>> codes;

        public Helper(List<EnumX<T>> codes)
        {
            this.codes = codes;
        }

        public string GetDescription(T code)
        {
            EnumX<T> e = codes.Where(c => c.Code.Equals(code)).FirstOrDefault();
            return e is null ? "Undefined" : e.Description;
        }
    }
}

Verwendungszweck:

EnumX<Status>.Helper StatusCodes = new EnumX<Status>.Helper(new List<EnumX<Status>>()
        {
            new EnumX<Status>(Status.Success,"Operation was successful"),
            new EnumX<Status>(Status.Fail,"Operation failed"),
            new EnumX<Status>(Status.Pending,"Operation not complete. Please wait...")
        });

        Console.WriteLine(StatusCodes.GetDescription(Status.Pending));
Zisha
quelle
-2

Ich denke, der beste (und einfachste) Weg, Ihr Problem zu lösen, besteht darin, eine Erweiterungsmethode für Ihre Aufzählung zu schreiben:

public static string GetUserFriendlyString(this PublishStatusses status)
    {

    }
user7096584
quelle
1
Jemand war 7 Jahre zuvor, um das zu sagen
Steven