【问题标题】:How to get all descriptions of enum values with reflection?如何通过反射获取枚举值的所有描述?
【发布时间】:2013-05-28 23:39:24
【问题描述】:

所以我需要从我的enum 获得一个List<string>

这是我到目前为止所做的:

枚举定义

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

HelperClass 及其方法可以满足我的需要:

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

现在在我 yield 值的行中,我得到了一个 NullReferenceException。我错过了什么?语法对我来说看起来不错,但也许我忽略了一些东西?

编辑: 我在这里使用 .net Framework 4.0。

【问题讨论】:

  • 既然这(显然)是一个标志枚举,那么您的“收件人/交付”实际上不应该是值 3 吗?比如,不应该是ContractorType.RECIPIENT | ContractorType.DELIVER的组合吗?否则(ContractorType.RECIPIENT | ContractorType.DELIVER) != ContractorType.RECIPIENT_DELIVER。也许您应该删除 [Flags] 属性。

标签: c# reflection enums ienumerable custom-attributes


【解决方案1】:

这个通用静态方法适用于获取枚举类型 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);
}

【讨论】:

    【解决方案2】:

    这是字典而不是列表
    但这是我使用的东西

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

    【讨论】:

      【解决方案3】:

      这是一个可重复使用的小型解决方案。这是一个抽象类,它将从 T 类型中提取 K 类型的所有属性。

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

      如果我们现在只想提取DescriptionAttribute 类型的属性,我们将使用以下类。

      class DescriptionAttributes<T> : AbstractAttributes<T, DescriptionAttribute>
      {
          public List<string> Descriptions { get; set; }
      
          public DescriptionAttributes()
          {
              Descriptions = Attributes.Select(x => x.Description).ToList();
          }
      }
      

      这个类将只从T 类型中提取DescriptionAttribute 类型的属性。但要在您的上下文中实际使用此类,您只需执行以下操作。

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

      这行代码将写出您在DescriptionAttribute 类型的属性中用作参数的所有描述。如果您需要提取一些其他属性,只需创建一个派生自 AbstractAttributes&lt;T, K&gt; 类的新类,并使用适当的属性关闭其类型 K。

      【讨论】:

        【解决方案4】:

        你可以试试这个

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

        【讨论】:

        • 是的,但您的解决方案仅适用于 1 种自定义 enum,示例中为 ContractorType 我想编写方法来处理应用程序中所有可能的自定义枚举器。所以这不是我的要求
        • 那么你必须确定枚举的类型,我认为上述答案之一使用它
        【解决方案5】:

        它认为这可以解决您的问题。如果未实现,您可以返回 null 或异常。这取决于你需要什么。

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

        所以你会这样使用它:

        DescriptionAttribute attribute = GetDescription(ContractorType.RECIPIENT);
        

        抱歉,我没有阅读您的问题。以下是一些可用于获取所有描述字符串的代码:

         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】:

          我创建了这些扩展方法

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

          用法

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

          将返回一个Dictionary&lt;TestEnum, string&gt;,其中值作为键,描述作为值。如果您只想要一个列表,您可以将.ToDictionary 更改为

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

          【讨论】:

            【解决方案7】:

            您需要在每个字段上找到DescriptionAttribute(如果存在),然后检索Description 属性,例如

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

            如果您可以在一个字段上有多个描述,您可以执行以下操作:

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

            这更类似于您现有的方法。

            【讨论】:

            • 没有GetCustomAttributes() 0 参数重载
            • @harry180 - 这是 .Net 4.5 中添加的扩展方法,因此如果您使用该版本的框架,则需要添加 using System.Reflection 指令。或者,您可以使用带有 bool 参数的重载并传递 false。我已经更新了使用它的答案。
            猜你喜欢
            • 1970-01-01
            • 2023-03-31
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多