Enum to list as an extension?

后端 未结 6 1310
花落未央
花落未央 2021-02-19 19:00

I have various enums that I use as sources for dropdown lists, In order to provide for a user-friendly description, I added a Description attribute to each enum, an

相关标签:
6条回答
  • 2021-02-19 19:32

    You can create a generic method which would take Enum and Attribute as generic argument.

    For getting any attribute, you can create an extension method like:

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

    and here is the method for converting it to dictionary:

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

    You can call it this way:

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

    I have used this Enum and Attribute as example:

    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
    }
    

    Output:

    0 讨论(0)
  • 2021-02-19 19:32

    Try replacing

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

    with

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

    In your example, the wrong overload of ToDictionary() is being called.

    0 讨论(0)
  • 2021-02-19 19:37

    What is the correct way to use it as an extension (using the above 2 methods)?

    There is no correct way to use it as an extension. Extension methods (similar to instance methods) are used when you have a value (instance) and for instance want to get some information related to that value. So the extension method would make sense if you want to get the description of a single enum value.

    However, in your case the information you need (the list of enum value/description pairs) is not tied to a specific enum value, but to the enum type. Which means you just need a plain static generic method similar to Enum.TryParse<TEnum>. Ideally you would constrain the generic argument to allow only enum, but this type of constraint is not supported (yet), so we'll use (similar to the above system method) just where TEnum : struct and will add runtime check.

    So here is a sample implementation:

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

    and usage:

    public enum MyEnum
    {
        [Description("Foo")]
        A,
        [Description("Bar")]
        B,
        [Description("Baz")]
        C,
    }
    
    var list = EnumInfo.GetList<MyEnum>();
    
    0 讨论(0)
  • 2021-02-19 19:41

    Another take on this:

    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;
        }
    }
    
    0 讨论(0)
  • 2021-02-19 19:44

    I have this extension method in my stack and use it for the same thing all the time.

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

    Example usage:

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

    Additionally, you can use this one to get a IDictionary for binding purposes.

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

    Use it like so:

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

    Update

    Here is a working .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());
    }
    

    Then use it like this:

    public enum Test
    {
        [Description("A test enum value for 'Foo'")]
        Foo,
        [Description("A test enum value for 'Bar'")]
        Bar
    }
    
    typeof(Test).ToDictionary<Test>()
    
    0 讨论(0)
  • 2021-02-19 19:45

    Whenever I need an enumeration (a static list of known values) that need to have something more than just a mere integer value and a string counterpart, I end up using this Enumeration Utility class that essentially gives me java-like enumeration behavior.

    So that would be my first option if I were on op's shoes as it would make it really trivial to achieve what he/she wants.

    But, assuming this is not an option for op and she/he need to stick with C# enums, I would use a combination of both ehsan-sajjad and frank-j solutions:

    1. Have an extension method to return the description of a given enum item, which is pretty much what op had already;
    2. Have a static helper method to return a dictionary of items and their respective descriptions for a given enum type.

    Here is how I would implement this:

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

    And here is what the usage would look like:

    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);
        }
    }
    
    0 讨论(0)
提交回复
热议问题