【问题标题】:Enum to list as an extension?枚举作为扩展列出?
【发布时间】:2016-03-24 19:14:54
【问题描述】:

我有各种枚举作为下拉列表的来源,为了提供用户友好的描述,我为每个枚举添加了一个Description 属性,然后执行以下操作:

var list = Enum.GetValues(typeof(MyEnum))
               .Cast<MyEnum>()
               .ToDictionary(k => k, v => v.GetAttributeOfType<DescriptionAttribute>().Description)
               .ToList();

以上是重复的,因为我必须在很多地方使用它。我尝试添加一个扩展方法:

    public static T GetAttributeOfType<T>(this Enum enumVal) where T : System.Attribute
    {
        var type = enumVal.GetType();
        var memInfo = type.GetMember(enumVal.ToString());
        var attributes = memInfo[0].GetCustomAttributes(typeof(T), false);

        return (attributes.Length > 0) ? (T)attributes[0] : null;
    }

    public static KeyValuePair<T, string> ToList<T>(this Enum source) 
    {
        return Enum.GetValues(typeof(T))
                   .Cast<T>()
                   .ToDictionary(k => k, v => v.GetAttributeOfType<DescriptionAttribute>().Description)
                   .ToList();
    }

但是,我得到了一个例外:

无法将 lambda 表达式转换为类型“System.Collections.Generic.IEqualityComparer”,因为它不是委托类型

将其用作扩展的正确方法是什么(使用上述2种方法)?

【问题讨论】:

  • 是的,有可能。
  • 不应该是Enum.GetValues(typeof(source))吗?
  • @Kevin 如果我这样做,我会得到一个异常:找不到命名空间名称“源”的类型。
  • 第二种方法(ToList)很奇怪。首先,它不会编译。其次,返回类型不明确——你传递一个Enum 值,然后使用ToDictionary(..).ToList(),它正在创建一个列表,返回类型是单个KeyValuePair。那么它到底应该是什么 - 单个值或列表?如果它是一个列表,那么作为扩展方法会很奇怪,所以你必须传递一个枚举值来获取列表,比如MyEnum.A.ToList&lt;MyEnum&gt;()。很快,您是否正在寻找与帖子开头的重复代码等效的代码?
  • 检查这个答案也可能有用:stackoverflow.com/a/12022617/1830909

标签: c#


【解决方案1】:

将其用作扩展的正确方法是什么(使用上述2种方法)?

没有将其用作扩展的正确方法。当你有一个(实例)并且例如想要获取与该值相关的一些信息时,使用扩展方法(类似于实例方法)。因此,如果您想获取单个 enum 值的描述,扩展方法将是有意义的。

但是,在您的情况下,您需要的信息(enum 值/描述对的列表)不与特定的 enum 值相关联,而是与enum 类型相关联。这意味着您只需要一个类似于Enum.TryParse&lt;TEnum&gt; 的普通static 泛型方法。理想情况下,您会将泛型参数限制为仅允许 enum,但(尚)不支持这种类型的约束,因此我们将仅使用(类似于上述系统方法)where TEnum : struct 并将添加运行时检查。

所以这里是一个示例实现:

public static class EnumInfo
{
    public static List<KeyValuePair<TEnum, string>> GetList<TEnum>()
        where TEnum : struct
    {
        if (!typeof(TEnum).IsEnum) throw new InvalidOperationException();
        return ((TEnum[])Enum.GetValues(typeof(TEnum)))
           .ToDictionary(k => k, v => ((Enum)(object)v).GetAttributeOfType<DescriptionAttribute>().Description)
           .ToList();
    }
}

及用法:

public enum MyEnum
{
    [Description("Foo")]
    A,
    [Description("Bar")]
    B,
    [Description("Baz")]
    C,
}

var list = EnumInfo.GetList<MyEnum>();

【讨论】:

    【解决方案2】:

    我的堆栈中有这个扩展方法,并且一直将它用于同一件事。

    public static string Description(this Enum @enum)
    {
        try
        {
            var @string = @enum.ToString();
    
            var attribute =
                @enum.GetType()
                     .GetField(@string)
                     .GetCustomAttribute<DescriptionAttribute>(false);
    
            return attribute != null ? attribute.Description : @string;
        }
        catch // Log nothing, just return an empty string
        {
            return string.Empty;
        }
    }
    

    示例用法:

    MyEnum.Value.Description(); // The value from within the description attr.
    

    此外,您可以使用这个来获取用于绑定目的的 IDictionary。

    public static IDictionary<string, string> ToDictionary(this Type type)
    {
        if (!type.IsEnum)
        {
            throw new InvalidCastException("'enumValue' is not an Enumeration!");
        }
    
        var names = Enum.GetNames(type);
        var values = Enum.GetValues(type);
    
        return Enumerable.Range(0, names.Length)
                         .Select(index => new
                         {
                             Key = names[index],
                             Value = ((Enum)values.GetValue(index)).Description()
                         })
                         .ToDictionary(k => k.Key, k => k.Value);
    }
    

    像这样使用它:

    var dictionary = typeof(MyEnum).ToDictionary();
    

    更新

    这是一个有效的.NET Fiddle

    public static Dictionary<TEnum, string> ToDictionary<TEnum>(this Type type)
        where TEnum : struct, IComparable, IFormattable, IConvertible
    {
        return Enum.GetValues(type)
                   .OfType<TEnum>()
                   .ToDictionary(value => value, value => value.Description());
    }
    

    然后像这样使用它:

    public enum Test
    {
        [Description("A test enum value for 'Foo'")]
        Foo,
        [Description("A test enum value for 'Bar'")]
        Bar
    }
    
    typeof(Test).ToDictionary<Test>()
    

    【讨论】:

    • 这个方法不是只返回一个枚举的字符串吗?
    • 你想调用 ToDictionary() 扩展方法。
    【解决方案3】:

    您可以创建一个通用方法,将EnumAttribute 作为通用参数。

    要获取任何属性,您可以创建一个扩展方法,例如:

    public static string AttributeValue<TEnum,TAttribute>(this TEnum value,Func<TAttribute,string> func) where T : Attribute
    {
       FieldInfo field = value.GetType().GetField(value.ToString());
    
       T attribute = Attribute.GetCustomAttribute(field, typeof(T)) as T;
    
       return attribute == null ? value.ToString() : func(attribute);
    
    }  
    

    这是将其转换为字典的方法:

    public static Dictionary<TEnum,string> ToDictionary<TEnum,TAttribute>(this TEnum obj,Func<TAttribute,string> func)
      where TEnum : struct, IComparable, IFormattable, IConvertible
      where TAttribute : Attribute
        {
    
            return (Enum.GetValues(typeof(TEnum)).OfType<TEnum>()
                .Select(x =>
                    new
                    {
                        Value = x,
                        Description = x.AttributeValue<TEnum,TAttribute>(func)
                    }).ToDictionary(x=>x.Value,x=>x.Description));
    
    
    
        }
    

    你可以这样称呼它:

     var test =  eUserRole.SuperAdmin
                          .ToDictionary<eUserRole,EnumDisplayNameAttribute>(attr=>attr.DisplayName); 
    

    我以这个枚举和属性为例:

    public class EnumDisplayNameAttribute : Attribute
    {
        private string _displayName;
        public string DisplayName
        {
            get { return _displayName; }
            set { _displayName = value; }
        }
    }  
    
    public enum eUserRole : int
    {
        [EnumDisplayName(DisplayName = "Super Admin")]
        SuperAdmin = 0,
        [EnumDisplayName(DisplayName = "Phoenix Admin")]
        PhoenixAdmin = 1,
        [EnumDisplayName(DisplayName = "Office Admin")]
        OfficeAdmin = 2,
        [EnumDisplayName(DisplayName = "Report User")]
        ReportUser = 3,
        [EnumDisplayName(DisplayName = "Billing User")]
        BillingUser = 4
    }
    

    输出:

    【讨论】:

    • 你丢失了字典中枚举的类型,将其替换为int
    • @FrankJ 我们可以轻松更改它,如果我们愿意,我更新了它
    • 我认为现在这是 OP 想要的 -> +1
    【解决方案4】:

    对此的另一种看法:

    class Program
    {
        //Example enum
        public enum eFancyEnum
        {
            [Description("Obsolete")]
            Yahoo,
            [Description("I want food")]
            Meow,
            [Description("I want attention")]
            Woof,
        }
        static void Main(string[] args)
        {
            //This is how you use it
            Dictionary<eFancyEnum, string> myDictionary = typeof(eFancyEnum).ToDictionary<eFancyEnum>();
        }
    }
    
    public static class EnumExtension
    {
        //Helper method to get description
        public static string ToDescription<T>(this T en)
        {
             Type type = en.GetType();
             MemberInfo[] memInfo = type.GetMember(en.ToString());
             if (memInfo != null && memInfo.Length > 0)
             {
                object[] attrs = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);
                if (attrs != null && attrs.Length > 0)
                   return ((DescriptionAttribute)attrs[0]).Description;
             }
             return en.ToString();
        }
    
        //The actual extension method that builds your dictionary
        public static Dictionary<T, string> ToDictionary<T>(this Type source) where T : struct, IConvertible
        {
             if(!source.IsEnum || typeof(T) != source)
             {
                throw new InvalidEnumArgumentException("BOOM");
             }
    
             Dictionary<T, string> retVal = new Dictionary<T,string>();
    
             foreach (var item in Enum.GetValues(typeof(T)).Cast<T>())
              {
                retVal.Add(item, item.ToDescription());
              }
    
             return retVal;
        }
    }
    

    【讨论】:

      【解决方案5】:

      每当我需要一个枚举(已知值的静态列表)时,我需要的不仅仅是一个整数值和一个字符串对应物,我最终会使用这个Enumeration Utility class,它本质上给了我类似 java 的枚举行为.

      因此,如果我站在 op 的立场上,那将是我的第一选择,因为这将使实现他/她想要的事情变得非常简单。

      但是,假设这不是 op 的选项并且她/他需要坚持使用 C# 枚举,我会使用 ehsan-sajjadfrank-j 的组合解决方案:

      1. 有一个扩展方法来返回给定枚举的描述 item,这几乎是 op 已经拥有的;
      2. 有一个静态辅助方法来返回给定枚举类型的项目字典及其各自的描述。

      这是我将如何实现的:

      public static class EnumUtils
      {
          public static string GetDescription(this Enum enumVal)
          {
              var type = enumVal.GetType();
              var memInfo = type.GetMember(enumVal.ToString());
              var attributes = memInfo[0].GetCustomAttributes(typeof (DescriptionAttribute), false);
      
              return (attributes.Length > 0) ? ((DescriptionAttribute) attributes[0]).Description : null;
          }
      
          public static Dictionary<TEnum, string> GetItemsWithDescrition<TEnum>()
          {
              var enumType = typeof(TEnum);
              if (!enumType.IsEnum)
              {
                  throw new InvalidOperationException("TEnum must be an enum type");
              }
      
              return Enum
                      .GetValues(enumType)
                      .Cast<TEnum>()
                      .ToDictionary(enumValue => enumValue, enumValue => GetDescription(enumValue as Enum));
          }
      }
      

      下面是用法的样子:

      public class EnumUtilsTests
      {
          public enum MyEnum
          {
              [Description("Um")]
              One,
              [Description("Dois")]
              Two,
              [Description("Tres")]
              Three,
              NoDescription
          }
      
          public void Should_get_enum_description()
          {
              MyEnum.One.GetDescription().ShouldBe("Um");
              MyEnum.Two.GetDescription().ShouldBe("Dois");
              MyEnum.Three.GetDescription().ShouldBe("Tres");
              MyEnum.NoDescription.GetDescription().ShouldBe(null);
          }
      
          public void Should_get_all_enum_values_with_description()
          {
              var response = EnumUtils.GetItemsWithDescrition<MyEnum>();
      
              response.ShouldContain(x => x.Key == MyEnum.One && x.Value == "Um");
              response.ShouldContain(x => x.Key == MyEnum.Two && x.Value == "Dois");
              response.ShouldContain(x => x.Key == MyEnum.Three && x.Value == "Tres");
              response.ShouldContain(x => x.Key == MyEnum.NoDescription && x.Value == null);
          }
      }
      

      【讨论】:

        【解决方案6】:

        尝试替换

        .ToDictionary(k => k, v => v.GetAttributeOfType<DescriptionAttribute>().Description)
        

        .Select(t => new { k = t, v = t.GetAttributeOfType<DescriptionAttribute>().Description)
        .ToDictionary(s => s.k, s => s.v)
        

        在您的示例中,调用了错误的 ToDictionary() 重载。

        【讨论】:

          猜你喜欢
          • 2010-12-10
          • 2012-07-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-08-03
          相关资源
          最近更新 更多