枚举作为扩展名列出?

时间:2016-03-24 19:14:55

标签: c#

我使用各种枚举作为下拉列表的来源。为了提供用户友好的描述,我为每个枚举添加了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',因为它不是委托类型

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

6 个答案:

答案 0 :(得分:6)

  

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

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

但是,在您的情况下,您需要的信息(enum值/说明对列表)与特定enum值无关,而与enum 无关输入即可。这意味着您只需要一个类似于Enum.TryParse<TEnum>的简单静态泛型方法。理想情况下,您会将泛型参数限制为仅允许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>();

答案 1 :(得分: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.

此外,您可以使用此ID来获取用于绑定目的的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>()

答案 2 :(得分:1)

您可以创建一个通用方法,将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
}

输出:

enter image description here

答案 3 :(得分:0)

另一种观点:

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

答案 4 :(得分:0)

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

因此,如果我选择这样的话,这将是我的第一选择,因为这样可以实现他/她想要的目标。

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

  1. 使用扩展方法返回给定枚举的描述 项目,这几乎是op已经有的;
  2. 使用静态帮助器方法返回项目字典及其对给定枚举类型的相应描述。
  3. 以下是我将如何实现这一点:

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

答案 5 :(得分:0)

尝试替换

.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()的错误重载。