Getting attributes of Enum's value
我想知道是否可以获取枚举值的属性而不是枚举本身的属性?例如,假设我有以下枚举:
1 2 3 4 5 6 7 8 9 | using System.ComponentModel; // for DescriptionAttribute enum FunkyAttributesEnum { [Description("Name With Spaces1")] NameWithoutSpaces1, [Description("Name With Spaces2")] NameWithoutSpaces2 } |
我想要的是给定枚举类型,生成枚举字符串值的2个元组及其描述。
价值是容易的:
1 2 3 |
但如何获取description属性的值来填充tuple.desc?如果属性属于枚举本身,我可以考虑如何执行它,但是对于如何从枚举的值中获取它,我感到茫然。
这应该能满足你的需要。
1 2 3 4 |
这段代码应该为您提供一个很好的任何枚举的小扩展方法,让您检索一个通用属性。我相信它与上面的lambda函数不同,因为它使用起来更简单,而且稍微有点——您只需要传入泛型类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public static class EnumHelper { /// <summary> /// Gets an attribute on an enum field value /// </summary> /// <typeparam name="T">The type of the attribute you want to retrieve</typeparam> /// <param name="enumVal">The enum value</param> /// <returns>The attribute of type T that exists on the enum value</returns> /// <example>string desc = myEnumVariable.GetAttributeOfType<DescriptionAttribute>().Description;</example> 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; } } |
这是一个使用lambda进行选择的通用实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public static Expected GetAttributeValue<T, Expected>(this Enum enumeration, Func<T, Expected> expression) where T : Attribute { T attribute = enumeration .GetType() .GetMember(enumeration.ToString()) .Where(member => member.MemberType == MemberTypes.Field) .FirstOrDefault() .GetCustomAttributes(typeof(T), false) .Cast<T>() .SingleOrDefault(); if (attribute == null) return default(Expected); return expression(attribute); } |
这样称呼它:
1 | string description = targetLevel.GetAttributeValue<DescriptionAttribute, string>(x => x.Description); |
我在这里合并了几个答案,以创建一个更具扩展性的解决方案。我提供它只是为了将来对任何人都有帮助。此处为原始发布。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | using System; using System.ComponentModel; public static class EnumExtensions { // This extension method is broken out so you can use a similar pattern with // other MetaData elements in the future. This is your base method for each. public static T GetAttribute<T>(this Enum value) where T : Attribute { var type = value.GetType(); var memberInfo = type.GetMember(value.ToString()); var attributes = memberInfo[0].GetCustomAttributes(typeof(T), false); return attributes.Length > 0 ? (T)attributes[0] : null; } // This method creates a specific call to the above method, requesting the // Description MetaData attribute. public static string ToName(this Enum value) { var attribute = value.GetAttribute<DescriptionAttribute>(); return attribute == null ? value.ToString() : attribute.Description; } } |
此解决方案在枚举上创建一对扩展方法。第一种方法允许您使用反射来检索与您的值相关联的任何属性。第二个调用专门检索
例如,考虑使用来自
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | using System.ComponentModel; public enum Days { [Description("Sunday")] Sun, [Description("Monday")] Mon, [Description("Tuesday")] Tue, [Description("Wednesday")] Wed, [Description("Thursday")] Thu, [Description("Friday")] Fri, [Description("Saturday")] Sat } |
要使用上述扩展方法,现在只需调用以下内容:
1 | Console.WriteLine(Days.Mon.ToName()); |
或
1 2 | var day = Days.Mon; Console.WriteLine(day.ToName()); |
除了adamcrawford响应之外,我还进一步创建了一个更专门的扩展方法,通过它来获取描述。
1 2 3 4 5 | public static string GetAttributeDescription(this Enum enumValue) { var attribute = enumValue.GetAttributeOfType<DescriptionAttribute>(); return attribute == null ? String.Empty : attribute.Description; } |
因此,要获取描述,可以使用原始扩展方法作为
1 | string desc = myEnumVariable.GetAttributeOfType<DescriptionAttribute>().Description |
或者您可以在这里简单地调用扩展方法,如下所示:
1 | string desc = myEnumVariable.GetAttributeDescription(); |
希望能让代码更可读。
流畅的一行…
这里我使用的是
1 2 3 4 5 6 | public static DisplayAttribute GetDisplayAttributesFrom(this Enum enumValue, Type enumType) { return enumType.GetMember(enumValue.ToString()) .First() .GetCustomAttribute<DisplayAttribute>(); } |
例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public enum ModesOfTransport { [Display(Name ="Driving", Description ="Driving a car")] Land, [Display(Name ="Flying", Description ="Flying on a plane")] Air, [Display(Name ="Sea cruise", Description ="Cruising on a dinghy")] Sea } void Main() { ModesOfTransport TransportMode = ModesOfTransport.Sea; DisplayAttribute metadata = TransportMode.GetDisplayAttributesFrom(typeof(ModesOfTransport)); Console.WriteLine("Name: {0} Description: {1}", metadata.Name, metadata.Description); } |
产量
1 2 | Name: Sea cruise Description: Cruising on a dinghy |
下面是从显示属性获取信息的代码。它使用一个通用方法来检索属性。如果找不到属性,则将枚举值转换为字符串,并将pascal/camel大小写转换为标题大小写(此处获取代码)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | public static class EnumHelper { // Get the Name value of the Display attribute if the // enum has one, otherwise use the value converted to title case. public static string GetDisplayName<TEnum>(this TEnum value) where TEnum : struct, IConvertible { var attr = value.GetAttributeOfType<TEnum, DisplayAttribute>(); return attr == null ? value.ToString().ToSpacedTitleCase() : attr.Name; } // Get the ShortName value of the Display attribute if the // enum has one, otherwise use the value converted to title case. public static string GetDisplayShortName<TEnum>(this TEnum value) where TEnum : struct, IConvertible { var attr = value.GetAttributeOfType<TEnum, DisplayAttribute>(); return attr == null ? value.ToString().ToSpacedTitleCase() : attr.ShortName; } /// <summary> /// Gets an attribute on an enum field value /// </summary> /// <typeparam name="TEnum">The enum type</typeparam> /// <typeparam name="T">The type of the attribute you want to retrieve</typeparam> /// <param name="value">The enum value</param> /// <returns>The attribute of type T that exists on the enum value</returns> private static T GetAttributeOfType<TEnum, T>(this TEnum value) where TEnum : struct, IConvertible where T : Attribute { return value.GetType() .GetMember(value.ToString()) .First() .GetCustomAttributes(false) .OfType<T>() .LastOrDefault(); } } |
这是字符串转换为标题大小写的扩展方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | /// <summary> /// Converts camel case or pascal case to separate words with title case /// </summary> /// <param name="s"></param> /// <returns></returns> public static string ToSpacedTitleCase(this string s) { //https://stackoverflow.com/a/155486/150342 CultureInfo cultureInfo = Thread.CurrentThread.CurrentCulture; TextInfo textInfo = cultureInfo.TextInfo; return textInfo .ToTitleCase(Regex.Replace(s, "([a-z](?=[A-Z0-9])|[A-Z](?=[A-Z][a-z]))","$1")); } |
从枚举获取字典。
1 2 3 4 5 6 | public static IDictionary<string, int> ToDictionary(this Type enumType) { return Enum.GetValues(enumType) .Cast<object>() .ToDictionary(v => ((Enum)v).ToEnumDescription(), k => (int)k); } |
现在这样称呼…
1 |
EnumDeccription Ext方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | public static string ToEnumDescription(this Enum en) //ext method { 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(); } public enum ActivityType { [Description("Drip Plan Email")] DripPlanEmail = 1, [Description("Modification")] Modification = 2, [Description("View")] View = 3, [Description("E-Alert Sent")] EAlertSent = 4, [Description("E-Alert View")] EAlertView = 5 } |
添加我的网络框架和网络核心解决方案。
我将其用于我的网络框架实现:
1 2 3 4 5 6 7 8 9 10 11 12 | public static class EnumerationExtension { public static string Description( this Enum value ) { // get attributes var field = value.GetType().GetField( value.ToString() ); var attributes = field.GetCustomAttributes( typeof( DescriptionAttribute ), false ); // return description return attributes.Any() ? ( (DescriptionAttribute)attributes.ElementAt( 0 ) ).Description :"Description Not Found"; } } |
这对NetCore不起作用,因此我将其修改为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public static class EnumerationExtension { public static string Description( this Enum value ) { // get attributes var field = value.GetType().GetField( value.ToString() ); var attributes = field.GetCustomAttributes( false ); // Description is in a hidden Attribute class called DisplayAttribute // Not to be confused with DisplayNameAttribute dynamic displayAttribute = null; if (attributes.Any()) { displayAttribute = attributes.ElementAt( 0 ); } // return description return displayAttribute?.Description ??"Description Not Found"; } } |
枚举示例:
1 2 3 4 5 | public enum ExportTypes { [Display( Name ="csv", Description ="text/csv" )] CSV = 0 } |
静态添加的示例用法:
1 | var myDescription = myEnum.Description(); |
这是AdamCrawford答案的.NET核心版本,使用system.reflection.typeextensions;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public static class EnumHelper { /// <summary> /// Gets an attribute on an enum field value /// </summary> /// <typeparam name="T">The type of the attribute you want to retrieve</typeparam> /// <param name="enumVal">The enum value</param> /// <returns>The attribute of type T that exists on the enum value</returns> /// <example>string desc = myEnumVariable.GetAttributeOfType<DescriptionAttribute>().Description;</example> public static T GetAttributeOfType<T>(this Enum enumVal) where T : System.Attribute { var type = enumVal.GetType(); var memInfo = type.GetMember(enumVal.ToString()); IEnumerable<Attribute> attributes = memInfo[0].GetCustomAttributes(typeof(T), false); return (T)attributes?.ToArray()[0]; } } |
我实现了这个扩展方法来从枚举值中获取描述。它适用于所有类型的枚举。
1 2 3 4 5 6 7 8 9 | public static class EnumExtension { public static string ToDescription(this System.Enum value) { FieldInfo fi = value.GetType().GetField(value.ToString()); var attributes = (DescriptionAttribute[])fi.GetCustomAttributes(typeof(DescriptionAttribute), false); return attributes.Length > 0 ? attributes[0].Description : value.ToString(); } } |
利用一些较新的C语言功能,您可以减少行数:
1 2 3 4 5 6 7 | public static TAttribute GetEnumAttribute<TAttribute>(this Enum enumVal) where TAttribute : Attribute { var memberInfo = enumVal.GetType().GetMember(enumVal.ToString()); return memberInfo[0].GetCustomAttributes(typeof(TAttribute), false).OfType<TAttribute>().FirstOrDefault(); } public static string GetEnumDescription(this Enum enumValue) => enumValue.GetEnumAttribute<DescriptionAttribute>()?.Description ?? enumValue.ToString(); |
此扩展方法将使用其xmlenumattribute获取枚举值的字符串表示形式。如果不存在xmlenumattribute,则返回到enum.toString()。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | public static string ToStringUsingXmlEnumAttribute<T>(this T enumValue) where T: struct, IConvertible { if (!typeof(T).IsEnum) { throw new ArgumentException("T must be an enumerated type"); } string name; var type = typeof(T); var memInfo = type.GetMember(enumValue.ToString()); if (memInfo.Length == 1) { var attributes = memInfo[0].GetCustomAttributes(typeof(System.Xml.Serialization.XmlEnumAttribute), false); if (attributes.Length == 1) { name = ((System.Xml.Serialization.XmlEnumAttribute)attributes[0]).Name; } else { name = enumValue.ToString(); } } else { name = enumValue.ToString(); } return name; } |
我这个答案是从一个枚举属性中设置一个组合框,这很好。
然后我需要对相反的代码进行编码,以便从框中获得所选内容并返回正确类型的枚举。
我还修改了代码来处理属性丢失的情况。
为了下一个人的利益,这是我的最终解决方案
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | public static class Program { static void Main(string[] args) { // display the description attribute from the enum foreach (Colour type in (Colour[])Enum.GetValues(typeof(Colour))) { Console.WriteLine(EnumExtensions.ToName(type)); } // Get the array from the description string xStr ="Yellow"; Colour thisColour = EnumExtensions.FromName<Colour>(xStr); Console.ReadLine(); } public enum Colour { [Description("Colour Red")] Red = 0, [Description("Colour Green")] Green = 1, [Description("Colour Blue")] Blue = 2, Yellow = 3 } } public static class EnumExtensions { // This extension method is broken out so you can use a similar pattern with // other MetaData elements in the future. This is your base method for each. public static T GetAttribute<T>(this Enum value) where T : Attribute { var type = value.GetType(); var memberInfo = type.GetMember(value.ToString()); var attributes = memberInfo[0].GetCustomAttributes(typeof(T), false); // check if no attributes have been specified. if (((Array)attributes).Length > 0) { return (T)attributes[0]; } else { return null; } } // This method creates a specific call to the above method, requesting the // Description MetaData attribute. public static string ToName(this Enum value) { var attribute = value.GetAttribute<DescriptionAttribute>(); return attribute == null ? value.ToString() : attribute.Description; } /// <summary> /// Find the enum from the description attribute. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="desc"></param> /// <returns></returns> public static T FromName<T>(this string desc) where T : struct { string attr; Boolean found = false; T result = (T)Enum.GetValues(typeof(T)).GetValue(0); foreach (object enumVal in Enum.GetValues(typeof(T))) { attr = ((Enum)enumVal).ToName(); if (attr == desc) { result = (T)enumVal; found = true; break; } } if (!found) { throw new Exception(); } return result; } } |
}
如果你想要完整的名单,你可以这样做
1 2 3 |
1 2 3 4 5 6 7 | public enum DataFilters { [Display(Name="Equals")] Equals = 1,// Display Name and Enum Name are same [Display(Name="Does Not Equal")] DoesNotEqual = 2, // Display Name and Enum Name are different } |
在这种情况下,它将产生错误1"等于"
1 2 3 4 5 | public static string GetDisplayName(this Enum enumValue) { var enumMember = enumValue.GetType().GetMember(enumValue.ToString()).First(); return enumMember.GetCustomAttribute<DisplayAttribute>() != null ? enumMember.GetCustomAttribute<DisplayAttribute>().Name : enumMember.Name; } |
因此,如果它是相同的,则返回枚举名称而不是显示名称,因为枚举成员.getCustomAttribute()如果DisplayName和枚举名称相同,则获取空…..
伙计们,如果有帮助的话,我会和你们分享我的解决方案:自定义属性定义:
1 2 3 4 5 6 7 8 9 | [AttributeUsage(AttributeTargets.Field,AllowMultiple = false)] public class EnumDisplayName : Attribute { public string Name { get; private set; } public EnumDisplayName(string name) { Name = name; } } |
现在,因为我在htmlhelper扩展的htmlhelper定义中需要它:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | public static class EnumHelper { public static string EnumDisplayName(this HtmlHelper helper,EPriceType priceType) { //Get every fields from enum var fields = priceType.GetType().GetFields(); //Foreach field skipping 1`st fieldw which keeps currently sellected value for (int i = 0; i < fields.Length;i++ ) { //find field with same int value if ((int)fields[i].GetValue(priceType) == (int)priceType) { //get attributes of found field var attributes = fields[i].GetCustomAttributes(false); if (attributes.Length > 0) { //return name of found attribute var retAttr = (EnumDisplayName)attributes[0]; return retAttr.Name; } } } //throw Error if not found throw new Exception("B??d podczas ustalania atrybutów dla typu ceny allegro"); } } |
希望它有帮助
或者,您可以执行以下操作:
1 2 3 4 5 | Dictionary<FunkyAttributesEnum, string> description = new Dictionary<FunkyAttributesEnum, string>() { { FunkyAttributesEnum.NameWithoutSpaces1,"Name With Spaces1" }, { FunkyAttributesEnum.NameWithoutSpaces2,"Name With Spaces2" }, }; |
并获得以下描述:
1 | string s = description[FunkyAttributesEnum.NameWithoutSpaces1]; |
在我看来,这是一种更有效的方式来做你想做的事情,因为不需要反思。
您还可以定义一个枚举值,如