How to get all descriptions of enum values with re

2019-03-30 06:52发布

问题:

So I need to get a List<string> from my enum

Here is what I have done so far:

enum definition

    [Flags]
    public enum ContractorType
    {
        [Description("Recipient")]
        RECIPIENT = 1,
        [Description("Deliver")]
        DELIVER = 2,
        [Description("Recipient / Deliver")]
        RECIPIENT_DELIVER = 4
    }

HelperClass with method to do what I need:

public static class EnumUtils
{
    public static IEnumerable<string> GetDescrptions(Type enumerator)
    {
        FieldInfo[] fi = enumerator.GetFields();

        List<DescriptionAttribute> attributes = new List<DescriptionAttribute>();
        foreach (var i in fi)
        {
            try
            {
                yield return attributes.Add(((DescriptionAttribute[])i.GetCustomAttributes(
                    typeof(DescriptionAttribute),
                    false))[0]);
            }
            catch { }
        }
        return new List<string>{"empty"};
    }
}

Now in the line where I yield values, I got a NullReferenceException. Did I miss something? The syntax looks all right to me, but maybe I overlooked something?

Edit: I'm using .net Framework 4.0 here.

回答1:

Here is a small reusable solution. This is an abstract class which will extract all the attributes of type K from type T.

abstract class AbstractAttributes<T, K>
{
    protected List<K> Attributes = new List<K>();

    public AbstractAttributes()
    {
        foreach (var member in typeof(T).GetMembers())
        {
            foreach (K attribute in member.GetCustomAttributes(typeof(K), true)) 
                Attributes.Add(attribute);                
        }
    }
}

Should we now want to extract only attributes of DescriptionAttribute type, we would use the following class.

class DescriptionAttributes<T> : AbstractAttributes<T, DescriptionAttribute>
{
    public List<string> Descriptions { get; set; }

    public DescriptionAttributes()
    {
        Descriptions = Attributes.Select(x => x.Description).ToList();
    }
}

This class will extract only attributes of DescriptionAttribute type from the type T. But to actually use this class in you context you will simply need to do the following.

new DescriptionAttributes<ContractorType>().Descriptions.ForEach(x => Console.WriteLine(x));

This line of code will write out all the descriptions you used as parameters in your attributes of type DescriptionAttribute. Should you need to extract some other attributes, just create a new class that derives from the AbstractAttributes<T, K> class and close its type K with the appropriate attribute.



回答2:

You need to find the DescriptionAttribute on each field, if it exists and then retrieve the Description attribute e.g.

return enumType.GetFields()
                .Select(f => (DescriptionAttribute)f.GetCustomAttribute(typeof(DescriptionAttribute)))
                .Where(a => a != null)
                .Select(a => a.Description)

If you could have multiple descriptions on a field, you could do something like:

FieldInfo[] fields = enumType.GetFields();
foreach(FieldInfo field in fields)
{
    var descriptionAttributes = field.GetCustomAttributes(false).OfType<DescriptionAttribute>();
    foreach(var descAttr in descriptionAttributes)
    {
        yield return descAttr.Description;
    }
}

which is more similar to your existing approach.



回答3:

This generic static method works fine for getting a list of descriptions for each value of an enum type of T:

public static IEnumerable<string> GetDescriptions<T>()
{
    var attributes = typeof(T).GetMembers()
        .SelectMany(member => member.GetCustomAttributes(typeof (DescriptionAttribute), true).Cast<DescriptionAttribute>())
        .ToList();

    return attributes.Select(x => x.Description);
}


回答4:

I created these extension methods

public static class EnumExtender
{
    public static string GetDescription(this Enum enumValue)
    {
        string output = null;
        Type type = enumValue.GetType();
        FieldInfo fi = type.GetField(enumValue.ToString());
        var attrs = fi.GetCustomAttributes(typeof(DescriptionAttribute), false) as DescriptionAttribute[];
        if (attrs.Length > 0) output = attrs[0].Description;
        return output;
    }

    public static IDictionary<T, string> GetEnumValuesWithDescription<T>(this Type type) where T : struct, IConvertible
    {
        if (!type.IsEnum)
        {
            throw new ArgumentException("T must be an enumerated type");
        }

        return type.GetEnumValues()
                .OfType<T>()
                .ToDictionary(
                    key => key,
                    val => (val as Enum).GetDescription()
                );
    }
}

Usage

var stuff = typeof(TestEnum).GetEnumValuesWithDescription<TestEnum>();

Will return a Dictionary<TestEnum, string> with value as keys and descriptions as values. If you want just a list, you can change .ToDictionary to

.Select(o => (o as Enum).GetDescription())
.ToList()


回答5:

It think this can solve your problem. If it is not implemented you can return null or an exception. It depends what you need.

public DescriptionAttribute GetDescription(ContractorType contractorType)
{
     MemberInfo memberInfo = typeof(ContractorType).GetMember(contractorType.ToString())
                                          .FirstOrDefault();

     if (memberInfo != null)
    {
         DescriptionAttribute attribute = (DescriptionAttribute) 
                 memberInfo.GetCustomAttributes(typeof(DescriptionAttribute), false)
                           .FirstOrDefault();
         return attribute;
    }

    //return null;
    //or

    throw new NotImplementedException("There is no description for this enum");
}

So you will use it like this :

DescriptionAttribute attribute = GetDescription(ContractorType.RECIPIENT);

Sorry that I didn't read your question. Here is some code that you can use to take all of the description strings:

 public IEnumerable<string> GetAllDescriptionInText()
 {
     List<string> descList = new List<string>();
     foreach (DescriptionAttribute desc in Enum.GetValues(typeof(DescriptionAttribute)))
     {
         descList.Add(GetDescription(desc).Value);
     }
     return descList;
 }


回答6:

You can try this

public string ContractorTypeDescription(Enum ContractorType)
{
    FieldInfo fi = ContractorType.GetType().GetField(ContractorType.ToString());
    var attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false);
    if (attributes.Length > 0)
    {
        return attributes[0].Description;
    }
    else
    {
        return ContractorType.ToString();
    }
}


回答7:

This is Dictionary not List
But is is something I use

using System.ComponentModel;
using System.Reflection;
using MyExtensions;

namespace MyExtensions
{
    public static class Extension
    {
        public static string GetDescriptionName(this Enum value)
        {
            Type type = value.GetType();
            string name = Enum.GetName(type, value);
            if (name == null)
                return null;
            else
            {
                FieldInfo field = type.GetField(name);
                if (field == null)
                    return name;
                else
                {
                    DescriptionAttribute attr =
                            Attribute.GetCustomAttribute(field,
                                typeof(DescriptionAttribute)) as DescriptionAttribute;
                    if (attr == null)
                        return name;
                    else
                        return attr.Description;
                }
            }
        }
    }
}

namespace EnumDescription
{
    class Program
    {
        public enum enumDateCond : byte 
        {
            [Description("Empty")]
            Null = 0,
            [Description("Not Empty")]
            NotNull = 1,
            EQ = 2, 
            LT = 3, 
            LE = 4, 
            GE = 14, 
            GT = 15 
        };
        static void Main(string[] args)
        {
            enumDateCond x = enumDateCond.Null;
            string description = x.GetDescriptionName();
            foreach (enumDateCond enm in Enum.GetValues(typeof(enumDateCond)))
            {
                description = enm.GetDescriptionName();
                Console.WriteLine(description);
            }
            Console.WriteLine("Dictionary");
            Dictionary<enumDateCond, string> DLenumDateCond = EnumToDictionary<enumDateCond>();
            foreach(enumDateCond key in DLenumDateCond.Keys)
            {
                Console.WriteLine(key.ToString() + " " + DLenumDateCond[key]);
            }
        }
        public static Dictionary<T, string> EnumToDictionary<T>()
            where T : struct
        {
            Type enumType = typeof(T);

            // Can't use generic type constraints on value types,
            // so have to do check like this
            if (enumType.BaseType != typeof(Enum))
                throw new ArgumentException("T must be of type System.Enum");

            Dictionary<T, string> enumDL = new Dictionary<T, string>();
            foreach (T enm in Enum.GetValues(enumType))
            {
                string name = Enum.GetName(enumType, enm);
                if (name != null)
                {
                    FieldInfo field = enumType.GetField(name);
                    if (field != null)
                    {
                        DescriptionAttribute attr =
                                Attribute.GetCustomAttribute(field,
                                    typeof(DescriptionAttribute)) as DescriptionAttribute;
                        if (attr != null)
                            name = attr.Description;
                    }
                }
                enumDL.Add(enm, name);
            }
            return enumDL;
        }
    }
}