web-dev-qa-db-fra.com

Comment créer une liste déroulante liée à une énumération avec un formatage de chaîne personnalisé pour les valeurs enum?

Dans l'article Enum ToString , une méthode est décrite pour utiliser l'attribut personnalisé DescriptionAttribute comme ceci:

Enum HowNice {
  [Description("Really Nice")]
  ReallyNice,
  [Description("Kinda Nice")]
  SortOfNice,
  [Description("Not Nice At All")]
  NotNice
}

Et ensuite, vous appelez une fonction GetDescription, en utilisant une syntaxe comme celle-ci:

GetDescription<HowNice>(NotNice); // Returns "Not Nice At All"

Mais cela ne m’aide pas vraiment lorsque je veux simplement remplir un ComboBox avec les valeurs d’un énum, ​​car je ne peux pas forcer le ComboBox à appeler GetDescription.

Ce que je veux a les conditions suivantes:

  • La lecture de (HowNice)myComboBox.selectedItem Renvoie la valeur sélectionnée en tant que valeur enum.
  • L'utilisateur doit voir les chaînes d'affichage conviviales, et pas seulement le nom des valeurs d'énumération. Ainsi, au lieu de voir "NotNice", l'utilisateur verrait "Not Nice At All".
  • Espérons que la solution nécessitera un minimum de modifications de code pour les énumérations existantes.

De toute évidence, je pourrais implémenter une nouvelle classe pour chaque énumération que je crée et surcharger sa ToString(), mais cela demande beaucoup de travail pour chaque énumération et je préférerais éviter cela.

Des idées?

Heck, je vais même dans un câlin comme une prime :-)

133
Shalom Craimer

Vous pouvez écrire un TypeConverter qui lit les attributs spécifiés pour les rechercher dans vos ressources. Ainsi, vous obtiendrez un support multilingue pour les noms d’affichage sans trop de soucis.

Examinez les méthodes ConvertFrom/ConvertTo du TypeConverter et utilisez la réflexion pour lire les attributs sur votre enum fields.

42
sisve

ComboBox a tout ce dont vous avez besoin: la propriété FormattingEnabled, que vous devez définir sur true, et l'événement Format, où vous aurez besoin de placer le formatage souhaité logique. Ainsi,

myComboBox.FormattingEnabled = true;
myComboBox.Format += delegate(object sender, ListControlConvertEventArgs e)
    {
        e.Value = GetDescription<HowNice>((HowNice)e.Value);
    }
85
Anton Gogolev

Non! Les énumérations sont des primitives et non des objets d'interface utilisateur; leur servir l'interface utilisateur dans .ToString () serait assez mauvais du point de vue de la conception. Vous essayez de résoudre le mauvais problème ici: le vrai problème est que vous ne voulez pas qu'Enum.ToString () apparaisse dans la liste déroulante!

Maintenant, c’est un problème très facile à résoudre! Vous créez un objet d'interface utilisateur pour représenter vos éléments de boîte à options:

sealed class NicenessComboBoxItem
{
    public string Description { get { return ...; } }
    public HowNice Value { get; private set; }

    public NicenessComboBoxItem(HowNice howNice) { Value = howNice; }
}

Ajoutez ensuite des instances de cette classe à la collection Items de votre liste déroulante et définissez les propriétés suivantes:

comboBox.ValueMember = "Value";
comboBox.DisplayMember = "Description";
45
Sander

TypeConverter. Je pense que c'est ce que je cherchais. Salut à tous Simon Svensson !

[TypeConverter(typeof(EnumToStringUsingDescription))]
Enum HowNice {
  [Description("Really Nice")]
  ReallyNice,
  [Description("Kinda Nice")]
  SortOfNice,
  [Description("Not Nice At All")]
  NotNice
}

Tout ce que j'ai besoin de changer dans mon enum actuel, c'est d'ajouter cette ligne avant leur déclaration.

[TypeConverter(typeof(EnumToStringUsingDescription))]

Une fois que j'ai fait cela, n'importe quelle énumération sera affichée en utilisant le DescriptionAttribute de ses champs.

Oh, et le TypeConverter serait défini comme ceci:

public class EnumToStringUsingDescription : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return (sourceType.Equals(typeof(Enum)));
    }

    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        return (destinationType.Equals(typeof(String)));
    }

    public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
    {
        return base.ConvertFrom(context, culture, value);
    }

    public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
    {
        if (!destinationType.Equals(typeof(String)))
        {
            throw new ArgumentException("Can only convert to string.", "destinationType");
        }

        if (!value.GetType().BaseType.Equals(typeof(Enum)))
        {
            throw new ArgumentException("Can only convert an instance of enum.", "value");
        }

        string name = value.ToString();
        object[] attrs = 
            value.GetType().GetField(name).GetCustomAttributes(typeof(DescriptionAttribute), false);
        return (attrs.Length > 0) ? ((DescriptionAttribute)attrs[0]).Description : name;
    }
}

Cela m'aide avec mon cas ComboBox, mais évidemment ne remplace pas réellement la ToString(). Je suppose que je vais régler pour cela en attendant ...

42
Shalom Craimer

En utilisant votre exemple d'énumération:

using System.ComponentModel;

Enum HowNice
{
    [Description("Really Nice")]
    ReallyNice,
    [Description("Kinda Nice")]
    SortOfNice,
    [Description("Not Nice At All")]
    NotNice
}

Créer une extension:

public static class EnumExtensions
{
    public static string Description(this Enum value)
    {
        var enumType = value.GetType();
        var field = enumType.GetField(value.ToString());
        var attributes = field.GetCustomAttributes(typeof(DescriptionAttribute),
                                                   false);
        return attributes.Length == 0
            ? value.ToString()
            : ((DescriptionAttribute)attributes[0]).Description;
    }
}

Ensuite, vous pouvez utiliser quelque chose comme ceci:

HowNice myEnum = HowNice.ReallyNice;
string myDesc = myEnum.Description();

Voir: http://www.blackwasp.co.uk/EnumDescription.aspx pour plus d'informations. Le mérite revient à Richrd Carr pour la solution

31
Tyler Durden

Vous pouvez créer une structure générique que vous pouvez utiliser pour toutes vos énumérations avec des descriptions. Avec les conversions implicites vers et depuis la classe, vos variables fonctionnent toujours comme l’énumération, à l’exception de la méthode ToString:

public struct Described<T> where T : struct {

    private T _value;

    public Described(T value) {
        _value = value;
    }

    public override string ToString() {
        string text = _value.ToString();
        object[] attr =
            typeof(T).GetField(text)
            .GetCustomAttributes(typeof(DescriptionAttribute), false);
        if (attr.Length == 1) {
            text = ((DescriptionAttribute)attr[0]).Description;
        }
        return text;
    }

    public static implicit operator Described<T>(T value) {
        return new Described<T>(value);
    }

    public static implicit operator T(Described<T> value) {
        return value._value;
    }

}

Exemple d'utilisation:

Described<HowNice> Nice = HowNice.ReallyNice;

Console.WriteLine(Nice == HowNice.ReallyNice); // writes "True"
Console.WriteLine(Nice); // writes "Really Nice"
8
Guffa

La meilleure façon de faire est de faire un cours.

class EnumWithToString {
    private string description;
    internal EnumWithToString(string desc){
        description = desc;
    }
    public override string ToString(){
        return description;
    }
}

class HowNice : EnumWithToString {

    private HowNice(string desc) : base(desc){}

    public static readonly HowNice ReallyNice = new HowNice("Really Nice");
    public static readonly HowNice KindaNice = new HowNice("Kinda Nice");
    public static readonly HowNice NotVeryNice = new HowNice("Really Mean!");
}

Je crois que c'est la meilleure façon de le faire.

Une fois remplie dans des listes déroulantes, la jolie ToString sera montrée, et le fait que personne ne puisse créer plus d'instances de votre classe en fait une énumération.

p.s. il faudra peut-être de légères corrections syntaxiques, je ne suis pas très bon en C #. (Mec Java)

5
jjnguy

Je ne pense pas que vous puissiez le faire sans simplement vous lier à un type différent - du moins, pas commodément. Normalement, même si vous ne pouvez pas contrôler ToString(), vous pouvez utiliser un TypeConverter pour effectuer un formatage personnalisé - mais le code IIRC le _System.ComponentModel_ ne respecte pas cela pour les enums.

Vous pouvez vous lier à un _string[]_ des descriptions ou à quelque chose qui ressemble essentiellement à une paire clé/valeur? (description/valeur) - quelque chose comme:

_class EnumWrapper<T> where T : struct
{
    private readonly T value;
    public T Value { get { return value; } }
    public EnumWrapper(T value) { this.value = value; }
    public string Description { get { return GetDescription<T>(value); } }
    public override string ToString() { return Description; }

    public static EnumWrapper<T>[] GetValues()
    {
        T[] vals = (T[])Enum.GetValues(typeof(T));
        return Array.ConvertAll(vals, v => new EnumWrapper<T>(v));
    }
}
_

Et puis lier à EnumWrapper<HowNice>.GetValues()

5
Marc Gravell

Impossible de remplacer le ToString () des énumérations en C #. Cependant, vous pouvez utiliser des méthodes d'extension.

public static string ToString(this HowNice self, int neverUsed)
{
    switch (self)
    {
        case HowNice.ReallyNice:
            return "Rilly, rilly Nice";
            break;
    ...

Bien sûr, vous devrez faire un appel explicite à la méthode, c'est-à-dire;

HowNice.ReallyNice.ToString(0)

Ce n'est pas une bonne solution, avec une instruction switch et tout le reste - mais cela devrait fonctionner et, espérons-le, sans beaucoup de réécritures ...

3
Björn

Etant donné que vous préférez ne pas créer de classe pour chaque enum, je vous recommande de créer un dictionnaire de la valeur enum/du texte d'affichage et de sa liaison.

Notez que cela dépend des méthodes de la méthode GetDescription de la publication d'origine.

public static IDictionary<T, string> GetDescriptions<T>()
    where T : struct
{
    IDictionary<T, string> values = new Dictionary<T, string>();

    Type type = enumerationValue.GetType();
    if (!type.IsEnum)
    {
        throw new ArgumentException("T must be of Enum type", "enumerationValue");
    }

    //Tries to find a DescriptionAttribute for a potential friendly name
    //for the enum
    foreach (T value in Enum.GetValues(typeof(T)))
    {
        string text = value.GetDescription();

        values.Add(value, text);
    }

    return values;
}
3
Richard Szalay

Pour faire suite à @scraimer answer, voici une version du convertisseur de type énumération-chaîne, qui prend également en charge les indicateurs:

    /// <summary>
/// A drop-in converter that returns the strings from 
/// <see cref="System.ComponentModel.DescriptionAttribute"/>
/// of items in an enumaration when they are converted to a string,
/// like in ToString().
/// </summary>
public class EnumToStringUsingDescription : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return (sourceType.Equals(typeof(Enum)));
    }

    public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
    {
        return (destinationType.Equals(typeof(String)));
    }

    public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
    {
        return base.ConvertFrom(context, culture, value);
    }

    public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
    {
        if (destinationType.Equals(typeof(String)))
        {
            string name = value.ToString();
            Type effectiveType = value.GetType();          

            if (name != null)
            {
                FieldInfo fi = effectiveType.GetField(name);
                if (fi != null)
                {
                    object[] attrs =
                    fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
                    return (attrs.Length > 0) ? ((DescriptionAttribute)attrs[0]).Description : name;
                }

            }
        }

        return base.ConvertTo(context, culture, value, destinationType);
    }

    /// <summary>
    /// Coverts an Enums to string by it's description. falls back to ToString.
    /// </summary>
    /// <param name="value">The value.</param>
    /// <returns></returns>
    public string EnumToString(Enum value)
    {
        //getting the actual values
        List<Enum> values = EnumToStringUsingDescription.GetFlaggedValues(value);
        //values.ToString();
        //Will hold results for each value
        List<string> results = new List<string>();
        //getting the representing strings
        foreach (Enum currValue in values)
        {
            string currresult = this.ConvertTo(null, null, currValue, typeof(String)).ToString();;
            results.Add(currresult);
        }

        return String.Join("\n",results);

    }

    /// <summary>
    /// All of the values of enumeration that are represented by specified value.
    /// If it is not a flag, the value will be the only value retured
    /// </summary>
    /// <param name="value">The value.</param>
    /// <returns></returns>
    private static List<Enum> GetFlaggedValues(Enum value)
    {
        //checking if this string is a flaged Enum
        Type enumType = value.GetType();
        object[] attributes = enumType.GetCustomAttributes(true);
        bool hasFlags = false;
        foreach (object currAttibute in attributes)
        {
            if (enumType.GetCustomAttributes(true)[0] is System.FlagsAttribute)
            {
                hasFlags = true;
                break;
            }
        }
        //If it is a flag, add all fllaged values
        List<Enum> values = new List<Enum>();
        if (hasFlags)
        {
            Array allValues = Enum.GetValues(enumType);
            foreach (Enum currValue in allValues)
            {
                if (value.HasFlag(currValue))
                {
                    values.Add(currValue);
                }
            }



        }
        else//if not just add current value
        {
            values.Add(value);
        }
        return values;
    }

}

Et une méthode d'extension pour l'utiliser:

    /// <summary>
    /// Converts an Enum to string by it's description. falls back to ToString
    /// </summary>
    /// <param name="enumVal">The enum val.</param>
    /// <returns></returns>
    public static string ToStringByDescription(this Enum enumVal)
    {
        EnumToStringUsingDescription inter = new EnumToStringUsingDescription();
        string str = inter.EnumToString(enumVal);
        return str;
    }
2
Avi Turner

Désolé d'avoir mis ce vieux fil en place.

J'irais de la manière suivante pour localiser enum, car il peut afficher des valeurs significatives et localisées pour l'utilisateur, et pas seulement une description, via un champ de texte de liste déroulante dans cet exemple.

Tout d'abord, je crée une méthode simple appelée OwToStringByCulture pour obtenir des chaînes localisées à partir d'un fichier de ressources globales. Dans cet exemple, il s'agit de BiBongNet.resx dans le dossier App_GlobalResources. Dans ce fichier de ressources, assurez-vous que toutes les chaînes sont identiques aux valeurs de l'énum (ReallyNice, SortOfNice, NotNice). Dans cette méthode, je passe dans le paramètre: resourceClassName qui est généralement le nom du fichier de ressources.

Ensuite, je crée une méthode statique pour remplir une liste déroulante avec enum comme source de données, appelée OwFillDataWithEnum. Cette méthode peut être utilisée avec n'importe quel enum plus tard.

Ensuite, dans la page avec une liste déroulante appelée DropDownList1, je mets dans le Page_Load la suivante une simple ligne de code pour remplir l’énumération de la liste déroulante.

 BiBongNet.OwFillDataWithEnum<HowNice>(DropDownList1, "BiBongNet");

C'est ça. Je pense qu'avec des méthodes simples comme celles-ci, vous pouvez remplir n'importe quel contrôle de liste avec n'importe quelle énumération, avec non seulement des valeurs descriptives, mais également du texte localisé à afficher. Vous pouvez faire toutes ces méthodes comme méthodes d'extension pour une meilleure utilisation.

J'espère que cette aide. Partager pour être partagé!

Voici les méthodes:

public class BiBongNet
{

        enum HowNice
        {
            ReallyNice,
            SortOfNice,
            NotNice
        }

        /// <summary>
        /// This method is for filling a listcontrol,
        /// such as dropdownlist, listbox... 
        /// with an enum as the datasource.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="ctrl"></param>
        /// <param name="resourceClassName"></param>
        public static void OwFillDataWithEnum<T>(ListControl ctrl, string resourceClassName)
        {
            var owType = typeof(T);
            var values = Enum.GetValues(owType);
            for (var i = 0; i < values.Length; i++)
            {
                //Localize this for displaying listcontrol's text field.
                var text = OwToStringByCulture(resourceClassName, Enum.Parse(owType, values.GetValue(i).ToString()).ToString());
                //This is for listcontrol's value field
                var key = (Enum.Parse(owType, values.GetValue(i).ToString()));
                //add values of enum to listcontrol.
                ctrl.Items.Add(new ListItem(text, key.ToString()));
            }
        }

        /// <summary>
        /// Get localized strings.
        /// </summary>
        /// <param name="resourceClassName"></param>
        /// <param name="resourceKey"></param>
        /// <returns></returns>
        public static string OwToStringByCulture(string resourceClassName, string resourceKey)
        {
                return (string)HttpContext.GetGlobalResourceObject(resourceClassName, resourceKey);
        }
}
1
BiBongNet

Je voudrais écrire une classe générique pour une utilisation avec n'importe quel type. J'ai utilisé quelque chose comme ça dans le passé:

public class ComboBoxItem<T>
{
    /// The text to display.
    private string text = "";
    /// The associated tag.
    private T tag = default(T);

    public string Text
    {
        get
        {
            return text;
        }
    }

    public T Tag
    {
        get
        {
            return tag;
        }
    }

    public override string ToString()
    {
        return text;
    }

    // Add various constructors here to fit your needs
}

En plus de cela, vous pouvez ajouter une "méthode d'usine" statique pour créer une liste d'éléments de liste déroulante à partir d'un type enum (à peu près identique à la méthode GetDescriptions que vous avez là-bas). Cela vous éviterait d'avoir à implémenter une entité pour chaque type d'enum et de fournir également un emplacement logique/agréable pour la méthode d'assistance "GetDescriptions" (personnellement, je l'appellerais FromEnum (T obj) ...

1
Dan C.

J'ai essayé cette approche et cela a fonctionné pour moi.

J'ai créé une classe wrapper pour enums et surchargé l'opérateur implicite afin de pouvoir l'affecter à des variables enum (dans mon cas, je devais lier un objet à une valeur ComboBox.).

Vous pouvez utiliser la réflexion pour formater les valeurs enum comme vous le souhaitez. Dans mon cas, je récupère le DisplayAttribute parmi les valeurs enum (si existantes).

J'espère que cela t'aides.

public sealed class EnumItem<T>
{
    T value;

    public override string ToString()
    {
        return Display;
    }

    public string Display { get; private set; }
    public T Value { get; set; }

    public EnumItem(T val)
    {
        value = val;
        Type en = val.GetType();
        MemberInfo res = en.GetMember(val.ToString())?.FirstOrDefault();
        DisplayAttribute display = res.GetCustomAttribute<DisplayAttribute>();
        Display = display != null ? String.Format(display.Name, val) : val.ToString();
    }

    public static implicit operator T(EnumItem<T> val)
    {
        return val.Value;
    }

    public static implicit operator EnumItem<T>(T val)
    {
        return new EnumItem<T>(val);
    }
}

EDIT:

Juste au cas où, j’utilise la fonction suivante pour obtenir les valeurs enum que j’utilise pour le DataSource du ComboBox

public static class Utils
{
    public static IEnumerable<EnumItem<T>> GetEnumValues<T>()
    {
        List<EnumItem<T>> result = new List<EnumItem<T>>();
        foreach (T item in Enum.GetValues(typeof(T)))
        {
            result.Add(item);
        }
        return result;
    }
}

Ce dont vous avez besoin est de transformer une énumération en ReadonlyCollection et de lier la collection à la liste déroulante (ou à tout autre contrôle activé par une paire clé-valeur)

Tout d'abord, vous avez besoin d'une classe pour contenir les éléments de la liste. Puisque tout ce dont vous avez besoin est la paire int/string, je vous suggère d'utiliser une interface et un combo de classe de base pour pouvoir implémenter les fonctionnalités dans n'importe quel objet de votre choix:

public interface IValueDescritionItem
{
    int Value { get; set;}
    string Description { get; set;}
}

public class MyItem : IValueDescritionItem
{
    HowNice _howNice;
    string _description;

    public MyItem()
    {

    }

    public MyItem(HowNice howNice, string howNice_descr)
    {
        _howNice = howNice;
        _description = howNice_descr;
    }

    public HowNice Niceness { get { return _howNice; } }
    public String NicenessDescription { get { return _description; } }


    #region IValueDescritionItem Members

    int IValueDescritionItem.Value
    {
        get { return (int)_howNice; }
        set { _howNice = (HowNice)value; }
    }

    string IValueDescritionItem.Description
    {
        get { return _description; }
        set { _description = value; }
    }

    #endregion
}

Voici l’interface et un exemple de classe qui l’implémente. Notez que la clé de la classe est fortement typée en Enum, et que les propriétés IValueDescritionItem sont implémentées de manière explicite (la classe peut donc avoir toutes les propriétés et vous Paire clé/valeur.

Maintenant, la classe EnumToReadOnlyCollection:

public class EnumToReadOnlyCollection<T,TEnum> : ReadOnlyCollection<T> where T: IValueDescritionItem,new() where TEnum : struct
{
    Type _type;

    public EnumToReadOnlyCollection() : base(new List<T>())
    {
        _type = typeof(TEnum);
        if (_type.IsEnum)
        {
            FieldInfo[] fields = _type.GetFields();

            foreach (FieldInfo enum_item in fields)
            {
                if (!enum_item.IsSpecialName)
                {
                    T item = new T();
                    item.Value = (int)enum_item.GetValue(null);
                    item.Description = ((ItemDescription)enum_item.GetCustomAttributes(false)[0]).Description;
                    //above line should be replaced with proper code that gets the description attribute
                    Items.Add(item);
                }
            }
        }
        else
            throw new Exception("Only enum types are supported.");
    }

    public T this[TEnum key]
    {
        get 
        {
            return Items[Convert.ToInt32(key)];
        }
    }

}

Donc tout ce dont vous avez besoin dans votre code est:

private EnumToReadOnlyCollection<MyItem, HowNice> enumcol;
enumcol = new EnumToReadOnlyCollection<MyItem, HowNice>();
comboBox1.ValueMember = "Niceness";
comboBox1.DisplayMember = "NicenessDescription";
comboBox1.DataSource = enumcol;

N'oubliez pas que votre collection est typée avec MyItem. Par conséquent, la valeur de la liste déroulante doit renvoyer une valeur enum si vous effectuez une liaison avec la propriété appropriée.

J'ai ajouté la propriété T this [Enum t] pour rendre la collection encore plus utile qu'un simple consommable combo, par exemple textBox1.Text = enumcol [HowNice.ReallyNice] .NicenessDescription;

Vous pouvez bien sûr choisir de transformer MyItem en une classe Key/Value utilisée uniquement pour cet objet, ignorant effectivement MyItem dans les arguments de type d'EnumToReadnlyCollection, mais vous seriez obligé de choisir int pour la clé (ce qui signifie obtenir combobox1.SelectedValue renverrait int et non le type enum). Vous contournez ce problème si vous créez une classe KeyValueItem pour remplacer MyItem, etc., etc.

1
Nikos
Enum HowNice {
  [Description("Really Nice")]
  ReallyNice,
  [Description("Kinda Nice")]
  SortOfNice,
  [Description("Not Nice At All")]
  NotNice
}

Pour résoudre ce problème, vous devez utiliser une méthode d'extension et un tableau de chaînes comme suit:

Enum HowNice {
  ReallyNice  = 0,
  SortOfNice  = 1,
  NotNice     = 2
}

internal static class HowNiceIsThis
{
 const String[] strings = { "Really Nice", "Kinda Nice", "Not Nice At All" }

 public static String DecodeToString(this HowNice howNice)
 {
   return strings[(int)howNice];
 }
}

Code simple et décodage rapide.

1
Sérgio

Vous pouvez utiliser PostSharp pour cibler Enum.ToString et ajouter le code supplémentaire que vous souhaitez. Cela ne nécessite aucune modification de code.

1
majkinetor

Créez une collection contenant ce dont vous avez besoin (par exemple, de simples objets contenant une propriété Value contenant la valeur HowNice enum et une propriété Description contenant GetDescription<HowNice>(Value) et databind le combo à cette collection.

Un peu comme ça:

Combo.DataSource = new EnumeratedValueCollection<HowNice>();
Combo.ValueMember = "Value";
Combo.DisplayMember = "Description";

quand vous avez une classe de collection comme ceci:

using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace Whatever.Tickles.Your.Fancy
{
    public class EnumeratedValueCollection<T> : ReadOnlyCollection<EnumeratedValue<T>>
    {
        public EnumeratedValueCollection()
            : base(ListConstructor()) { }
        public EnumeratedValueCollection(Func<T, bool> selection)
            : base(ListConstructor(selection)) { }
        public EnumeratedValueCollection(Func<T, string> format)
            : base(ListConstructor(format)) { }
        public EnumeratedValueCollection(Func<T, bool> selection, Func<T, string> format)
            : base(ListConstructor(selection, format)) { }
        internal EnumeratedValueCollection(IList<EnumeratedValue<T>> data)
            : base(data) { }

        internal static List<EnumeratedValue<T>> ListConstructor()
        {
            return ListConstructor(null, null);
        }

        internal static List<EnumeratedValue<T>> ListConstructor(Func<T, string> format)
        {
            return ListConstructor(null, format);
        }

        internal static List<EnumeratedValue<T>> ListConstructor(Func<T, bool> selection)
        {
            return ListConstructor(selection, null);
        }

        internal static List<EnumeratedValue<T>> ListConstructor(Func<T, bool> selection, Func<T, string> format)
        {
            if (null == selection) selection = (x => true);
            if (null == format) format = (x => GetDescription<T>(x));
            var result = new List<EnumeratedValue<T>>();
            foreach (T value in System.Enum.GetValues(typeof(T)))
            {
                if (selection(value))
                {
                    string description = format(value);
                    result.Add(new EnumeratedValue<T>(value, description));
                }
            }
            return result;
        }

        public bool Contains(T value)
        {
            return (Items.FirstOrDefault(item => item.Value.Equals(value)) != null);
        }

        public EnumeratedValue<T> this[T value]
        {
            get
            {
                return Items.First(item => item.Value.Equals(value));
            }
        }

        public string Describe(T value)
        {
            return this[value].Description;
        }
    }

    [System.Diagnostics.DebuggerDisplay("{Value} ({Description})")]
    public class EnumeratedValue<T>
    {
        private T value;
        private string description;
        internal EnumeratedValue(T value, string description) {
            this.value = value;
            this.description = description;
        }
        public T Value { get { return this.value; } }
        public string Description { get { return this.description; } }
    }

}

Comme vous pouvez le constater, cette collection est facilement personnalisable avec lambda pour sélectionner un sous-ensemble de votre énumérateur et/ou implémenter une mise en forme personnalisée à string au lieu d'utiliser la fonction GetDescription<T>(x) que vous mentionnez.

1
peSHIr
Enum HowNice {   
[StringValue("Really Nice")]   
ReallyNice,   
[StringValue("Kinda Nice")]   
SortOfNice,   
[StringValue("Not Nice At All")]   
NotNice 
}

Status = ReallyNice.GetDescription()
0
user1308805

Une fois que vous avez la méthode GetDescription (elle doit être globale statique), vous pouvez l’utiliser via une méthode d’extension:

public static string ToString(this HowNice self)
{
    return GetDescription<HowNice>(self);
}
0
awe