Énumérer ToString avec des chaînes conviviales

283

Mon énumération comprend les valeurs suivantes:

private enum PublishStatusses{
    NotCompleted,
    Completed,
    Error
};

Je veux cependant pouvoir afficher ces valeurs de manière conviviale.
Je n'ai pas besoin de pouvoir à nouveau passer de la chaîne à la valeur.

Boris Callens
la source
doublon possible des
énumérations de
Reproduction possible de la représentation sous forme
Liam

Réponses:

350

J'utilise l' Descriptionattribut de l'espace de noms System.ComponentModel. Décorez simplement l'énumération:

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

Utilisez ensuite ce code pour le récupérer:

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
la source
12
Cet exemple est plus facile à lire. stackoverflow.com/questions/1415140/…
RayLoveless
31
Je soupçonne qu'il y a un impact significatif sur les performances pour l'utilisation de la réflexion comme décrit dans cette solution. Le code de la méthode utilisée par Will pour utiliser la méthode d'extension ToFriendlyString est beaucoup plus facile à comprendre et ses performances devraient également être extrêmement rapides.
humbads
1
J'aime la version que @RayL a liée car elle n'ajoutera que la méthode d'extension à Enums. Si c'est tout ce que vous voulez utiliser pour cela (comme indiqué avec le ArgumentException, alors il n'y a aucune raison pour que la méthode soit complètement générique.
krillgar
4
Cela signifie que chaque énumération a besoin de sa propre méthode d'extension. Ceci est une utilisation plus générale et nécessite plus de travail, mais vous voudrez probablement quantifier ce que signifie "rapide" avant de décider des performances.
Ray Booysen
2
@petar qui fonctionne mais pas si vous souhaitez que les chaînes conviviales soient affichées pour les utilisateurs. MY_TYPE aura le trait de soulignement et n'est pas personnalisable.
Ray Booysen
354

Je le fais avec des méthodes d'extension:

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

la source
6
C'est tellement plus propre que la réponse Attribut. Agréable!
pennyrave
3
@pennyrave: Eh. De nombreux composants de l'interface utilisateur attendent de trouver et d'utiliser DisplayNameAttribute et DescriptionAttribute. En fait, maintenant, j'utilise ces derniers et une méthode d'extension pour obtenir facilement ces valeurs.
60
Le problème que je vois avec cela est que vous écrivez constamment ces méthodes d'extension. Avec le mécanisme d'attribut, c'est un moyen simple de le décorer et d'appeler une seule méthode.
Ray Booysen
5
Pas sûr de ce que vous voulez dire?
Ray Booysen
9
Il est préférable, à mon avis, de permettre à l' defaultimplémentation de cas de revenir me.ToString()et de fournir uniquement des instructions de casse de commutateur pour les valeurs d'énumération que vous souhaitez remplacer. Dans votre exemple, je comprends qu'ils sont tous différents, mais dans les cas d'utilisation réels, je soupçonne que la plupart des valeurs d'énumération d'un seul mot suffiront et vous ne fournirez que des remplacements pour les valeurs d'énumération de plusieurs mots.
Scott
78

Peut-être que je manque quelque chose, mais quel est le problème avec Enum.GetName?

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

edit: pour des chaînes conviviales, vous devez passer par un .resource pour effectuer l'internationalisation / localisation, et il serait sans doute préférable d'utiliser une clé fixe basée sur la clé enum qu'un attribut décorateur sur le même.

annakata
la source
12
Je renvoie la valeur littérale de l'énumération, pas une valeur conviviale.
Boris Callens
2
oic - eh bien il y a un assez gros cas où vous devez passer par une bibliothèque de ressources de chaîne basée sur cette valeur, car l'alternative (attribut de décorateur) ne supportera pas I18N
annakata
1
Dans le cas de I18N, je ferais la recherche de la méthode GetDescription () dans la bibliothèque de ressources pour une chaîne traduite et reviendrais à la description, puis retomberais au littéral.
Boris Callens,
3
+1 pour MyEnum.ToString () comme clé de ressource pour la localisation. je fais ça depuis des années
jackvsworld
1
@annakata nous avons en fait étendu le mécanisme d'attribut pour inclure le support de l18N, c'est un simple changement en fait.
Ray Booysen
23

J'ai créé une méthode d'extension inverse pour reconvertir la description en une valeur énumérée:

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
la source
15
Je suis désolé, mais merci d'avoir essayé d'être utile! Bien qu'il s'agisse d'un site de questions / réponses, les réponses doivent être une tentative de répondre directement à la question. Et la question précise: « Je n'ai plus besoin de pouvoir passer de la chaîne à la valeur. » Encore une fois, merci!
Jesse
8
Merci pour la critique positive. Il est toujours difficile d'être nouveau sur un site et de découvrir sa culture et ses nuances. Je suis content qu'il y ait des gens comme vous qui mettent les nouveaux mecs au clair. Encore une fois, merci de ne pas vous être débarrassé du nouveau gars.
Brian Richardson
6
@Jesse Et 4 ans plus tard, quelqu'un est heureux de trouver le code bjrichardson ici! SO peut être un site de questions-réponses, mais cela ne signifie pas que les questions sont mortes après avoir reçu une réponse.
John
18

La solution la plus simple ici consiste à utiliser une méthode d'extension personnalisée (dans .NET 3.5 au moins - vous pouvez simplement la convertir en une méthode d'assistance statique pour les versions de framework antérieures).

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

Je suppose ici que vous souhaitez renvoyer autre chose que le nom réel de la valeur d'énumération (que vous pouvez obtenir en appelant simplement ToString).

Noldorin
la source
Bien que valide, j'aime davantage l'attribut. De cette façon, je peux mettre ma méthode toSTring dans une bibliothèque séparée, tout en plaçant la représentation de chaîne personnalisée avec l'énum lui
Boris Callens
1
C'est suffisant. Je suppose qu'un avantage de cette méthode est que vous pouvez inclure un argument avec la méthode spécifiant une variable d'état, puis changer la représentation de chaîne renvoyée en fonction de cela.
Noldorin
1
Oui, tout dépend de la portée de la méthode, je suppose. Alors que la méthode Attribute est plus générique, votre solution est plus localisée. Tout dépend des besoins au final.
Boris Callens
1
Vous pouvez placer des méthodes d'extension où vous le souhaitez. Il vous suffit de le référencer là où vous souhaitez les utiliser.
Oui, mais cela signifierait que cette méthode d'extension devrait être réécrite chaque fois que vous introduisez une nouvelle énumération pour laquelle vous souhaitez avoir un nom convivial. Cela signifierait également que TOUTES vos applications porteraient des noms conviviaux pour TOUTES vos autres applications ...
Boris Callens
13

Cet autre poste est Java. Vous ne pouvez pas mettre de méthodes dans Enums en C #.

faites juste quelque chose comme ça:

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

Si vous souhaitez utiliser différentes valeurs d'affichage pour vos valeurs d'énumération, vous pouvez utiliser Attributs et Réflexion.

Lemmy
la source
3
toString n'est pas sûr dans tous les cas - une énumération avec plusieurs entrées avec la même valeur (disons pour les énumérations entières) renverra la clé de la première valeur correspondante, pas la clé de l'élément testé, c'est pourquoi Enum.GetName est préféré
annakata
4
Eh bien, c'était la solution la plus simple pour son énumération spécifique
Lemmy
9

Le moyen le plus simple consiste simplement à inclure cette classe d'extension dans votre projet, elle fonctionnera avec n'importe quelle énumération du projet:

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

Usage:

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

...

ExampleEnum ee = ExampleEnum.Live;
Console.WriteLine(ee.ToFriendlyString());
Milan Švec
la source
2
C'est un mystère de savoir pourquoi ce commentaire n'est pas accepté, ou le plus voté - pas de réflexion, pas d'attributs inutiles, idéal pour les situations simples où l'énumération est déjà joliment nommée. Vous pouvez pousser cette réponse un peu plus loin et autoriser l'ajout d'espaces entre les majuscules avant de revenir, 'My Enum'.
Vix
12
Si l'énumération est déjà bien nommée, aucune méthode d'extension n'est nécessaire. Utilisez simplement la méthode ToString () existante. string result = "Result: " + ee;
John
Ce devrait être la meilleure réponse. Cela fonctionne pour n'importe quelle énumération. Vous pouvez même l'implémenter en utilisant une énumération spécifique en changeant simplement le type d'énumération du paramètre en énumération réelle sur laquelle l'utiliser.
Juanu Haedo
6
Cette réponse et tous les commentaires ignorent la demande d'origine pour une description étendue. Vous avez totalement raté l'exercice qui consiste à renvoyer autre chose que la valeur ToString par défaut. Je ne vais pas sous-évaluer toutes les notes de cette réponse ici, mais je le veux vraiment.
TonyG
8

Quelques autres options plus primitives qui évitent les classes / types de référence:

  • Méthode de tableau
  • Méthode de structure imbriquée

Méthode de tableau

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

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

Usage

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

Méthode de structure imbriquée

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

Usage

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

Mise à jour (03/09/2018)

Un hybride de méthodes d'extension et la première technique ci-dessus.

Je préfère que les énumérations soient définies là où elles "appartiennent" (les plus proches de leur source d'origine et non dans un espace de noms global commun).

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

La méthode d'extension semble adaptée à un espace commun, et la définition "localisée" de l'énumération rend maintenant la méthode d'extension plus verbeuse.

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

Un exemple d'utilisation de l'énumération et de sa méthode d'extension.

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

Remarque: J'ai en fait décidé d'éliminer le Enumwrapper (et le Nametableau), car il est préférable que les chaînes de noms proviennent d'une ressource (c'est-à-dire un fichier de configuration ou une base de données) au lieu d'être codées en dur, et parce que j'ai fini par mettre la méthode d'extension dans le ViewModelsespace de noms (juste dans un autre fichier "CommonVM.cs"). De plus, le tout .Iddevient distrayant et encombrant.

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

Un exemple d'utilisation de l'énumération et de sa méthode d'extension.

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 est
la source
+ 1-1 = 0 vote: Cette solution préserve la syntaxe Enum et résout élégamment le problème sans réflexion ni code complexe, donc +1 là-bas. Mais il perd les fonctionnalités d'Enums elles-mêmes. Ainsi, bien que l'OMI soit une bonne option, elle ne répond pas à la question réelle et obtient un -1. Net 0. Désolé, nous n'avons aucun moyen d'enregistrer cela mieux dans SO.
TonyG
@TonyG Assez juste. Après avoir manqué quelques questions sur l'évaluation des compétences .net de pluarlsight.com, j'ai commencé à réaliser à quel point les énumérations C # sont approfondies.C'est donc probablement une bonne idée de connaître au moins leurs capacités avant de décider de la méthodologie à appliquer (en particulier pour une utilisation généralisée, la refactorisation). peut être un peu de temps;).
samis
7

Vous pouvez utiliser Humanizer package avec Humanize énumérations possiblity. Un exemple:

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

alors vous pouvez utiliser Humanizedirectement la méthode d'extension sur enum:

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
la source
Il utilise également la réflexion et n'est pas mis en cache. github.com/Humanizr/Humanizer/blob/…
Konrad
Ce sera aussi lent que la solution dans la première réponse de Ray
Konrad
5

En ce qui concerne Ray Booysen, il y a un bogue dans le code: Enum ToString avec des chaînes conviviales

Vous devez tenir compte de plusieurs attributs sur les valeurs d'énumération.

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
la source
4
L'omission d'une vérification pour plusieurs attributs Description est délibérée. Si l'énumération en a deux et que vous utilisez pour générer une description, j'aimerais penser que c'est une circonstance exceptionnelle. Je pense que le bug réel est que je ne fais pas de Single () pour avoir une exception levée. Sinon, toute la signature de la méthode n'a aucun sens. GetDescription ()? Quelle description? Un agrégat?
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
la source
3
Il est toujours agréable d'écrire un texte expliquant pourquoi cela devrait fonctionner et pourquoi les PO ne l'étaient pas.
phaberest
Juste pour info, les conventions de code C # veulent des variables locales et des paramètres de méthode avec une lettre initiale en minuscule. Une exception est le thisparamètre dans les méthodes d'extension, que vous pouvez voir appelé Thisdans de nombreux exemples sur le Web. L'appeler comme son type comme vous l'avez fait ( Enum Enum) rend le code moins lisible.
Massimiliano Kraus
4

Au lieu d'utiliser une énumération, utilisez une classe statique.

remplacer

private enum PublishStatuses{
    NotCompleted,
    Completed,
    Error
};

avec

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

il sera utilisé comme ça

PublishStatuses.NotCompleted; // "Not Completed"

Problème en utilisant les meilleures solutions de "méthode d'extension":

Une énumération privée est souvent utilisée dans une autre classe. La solution de méthode d'extension n'est pas valide car elle doit être dans sa propre classe. Cette solution peut être privée et intégrée dans une autre classe.

Guirlande Asher
la source
2

Il se trouve que je suis un fan de VB.NET, alors voici ma version, combinant la méthode DescriptionAttribute avec une méthode d'extension. Premièrement, les résultats:

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

Trucs de base: une énumération appelée EnumType avec trois valeurs V1, V2 et V3. La "magie" se produit dans l'appel Console.WriteLine dans Sub Main (), où le dernier argument est simplement v.Description. Cela renvoie "Un" pour V1, "V2" pour V2 et "Trois" pour V3. Cette description-méthode est en fait une méthode d'extension, définie dans un autre module appelé EnumExtensions:

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

Parce que la recherche d'attributs de description à l'aide Reflection est lente, les recherches sont également mises en cache dans un environnement privé.Dictionary , qui est rempli à la demande.

(Désolé pour la solution VB.NET - il devrait être relativement simple de la traduire en C #, et mon C # est rouillé sur de nouveaux sujets comme les extensions)

MarkusT
la source
2

Résumé clair des suggestions ci-dessus avec un échantillon:

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

USAGE:

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
souligner
la source
1
Il y a un attribut [Description ("")] en C #, pourquoi ne pas l'utiliser?
Stefan Koenen
Bien sûr, utiliser [Description ("")] est une solution. Mais je voulais que l'échantillon soit complet.
souligner
2

Utilisez Enum.GetName

À partir du lien ci-dessus ...

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
la source
2

Selon cette documentation: https://docs.microsoft.com/pt-br/dotnet/api/system.enum.tostring?view=netframework-4.8

Il est possible de convertir simplement un énumérateur en chaîne en utilisant un format comme celui-ci:

public enum Example
{
    Example1,
    Example2
}

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

//Outputs: "Example1"

Vous pouvez voir tous les formats possibles dans ce lien: https://docs.microsoft.com/pt-br/dotnet/api/system.string?view=netframework-4.8

Emanuel Hiroshi
la source
1

Il s'agit d'une mise à jour du code de Ray Booysen qui utilise la méthode générique GetCustomAttributes et LINQ pour rendre les choses un peu plus ordonnées.

    /// <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
la source
Vous ne voyez pas pourquoi vous en avez besoin pour être générique? Si vous allez utiliser la réflexion?
Lee Louviere
@LeeLouviere Principalement pour éviter la boxe lorsque la struct (type de valeur) est passée en paramètre.
Richard Anthony Hein
1
à la place numerationValue.GetType () utilisez: typeof (T).
Slava
1
Grande amélioration d'une ligne par rapport à la réponse acceptée sans (YMMV) perdre la lisibilité. Oui, avec typeof (T).
TonyG
1

Résumé encore plus net:

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

Même utilisation que le trait de soulignement décrit.

StefanDK
la source
0

Pour les drapeaux enum y compris.

    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
la source
0

Dans le cas où vous souhaitez simplement ajouter un espace entre les mots, c'est aussi simple que

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

J'utilise une classe générique pour stocker les paires énum / description et une classe d'aide imbriquée pour obtenir la description.

L' énumération :

enum Status { Success, Fail, Pending }

La classe générique:

Remarque: Puisqu'une classe générique ne peut pas être contrainte par une énumération, je contrains par struct à la place et vérifie l' énumération dans le constructeur.

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

Usage:

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
la source
-2

Je pense que la meilleure façon (et la plus simple) de résoudre votre problème est d'écrire une méthode d'extension pour votre énumération:

public static string GetUserFriendlyString(this PublishStatusses status)
    {

    }
user7096584
la source
1
Quelqu'un était 7 ans plus tôt pour déclarer que
Steven