Получение атрибутов значения перечисления
Я хотел бы знать, можно ли получить атрибуты значений перечисления, а не самого перечисления? Например, предположим, что у меня есть следующее перечисление:
using System.ComponentModel; // for DescriptionAttribute
enum FunkyAttributesEnum
{
[Description("Name With Spaces1")]
NameWithoutSpaces1,
[Description("Name With Spaces2")]
NameWithoutSpaces2
}
то, что я хочу, задается типом перечисления, создает 2-кортежи строкового значения перечисления и его описание.
значение было легко:
Array values = System.Enum.GetValues(typeof(FunkyAttributesEnum));
foreach (int value in values)
Tuple.Value = Enum.GetName(typeof(FunkyAttributesEnum), value);
но как я могу получить значение атрибута, чтобы заполнить кортежа.Деск? Я могу придумать как это сделать, если атрибут принадлежит перечисления, но я я в недоумении относительно того, как получить его от значения перечисления.
19 ответов:
Это должно делать то, что вам нужно.
var type = typeof(FunkyAttributesEnum); var memInfo = type.GetMember(FunkyAttributesEnum.NameWithoutSpaces1.ToString()); var attributes = memInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false); var description = ((DescriptionAttribute)attributes[0]).Description;
этот фрагмент кода должен дать вам хороший метод расширения для любого перечисления, который позволяет получить универсальный атрибут. Я считаю, что это отличается от функции лямбда выше, потому что она проще в использовании и немного - вам нужно только передать общий тип.
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; } }
это общая реализация с использованием лямбда для выбора
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); }назовем это так:
string description = targetLevel.GetAttributeValue<DescriptionAttribute, string>(x => x.Description);
Я объединил несколько ответов здесь, чтобы создать немного более расширяемое решение. Я предоставляю его на всякий случай, если это будет полезно кому-то еще в будущем. Оригинальная публикация здесь.
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 (T)attributes[0]; } // 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; } }это решение создает пару методов расширения на перечисление. Первый позволяет использовать отражение для получения любого атрибута, связанного с вашим значением. Второй, в частности, предусматривает получение
DescriptionAttributeи возвращает этоDescriptionзначение.как например, рассмотрите возможность использования С
System.ComponentModelusing 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 }чтобы использовать вышеуказанный метод расширения, теперь вы просто вызовете следующее:
Console.WriteLine(Days.Mon.ToName());или
var day = Days.Mon; Console.WriteLine(day.ToName());
кроме AdamCrawford ответ, Я далее создал более специализированные методы расширения, которые кормят его, чтобы получить описание.
public static string GetAttributeDescription(this Enum enumValue) { var attribute = enumValue.GetAttributeOfType<DescriptionAttribute>(); return attribute == null ? String.Empty : attribute.Description; }следовательно, чтобы получить описание, вы можете использовать исходный метод расширения как
string desc = myEnumVariable.GetAttributeOfType<DescriptionAttribute>().Descriptionили вы можете просто вызвать метод расширения здесь как:
string desc = myEnumVariable.GetAttributeDescription();, который, надеюсь, сделает ваш код немного более читаемым.
владеет один лайнер...
здесь я использую
DisplayAttribute, которая содержитNameиDescriptionсвойства.public static DisplayAttribute GetDisplayAttributesFrom(this Enum enumValue, Type enumType) { return enumType.GetMember(enumValue.ToString()) .First() .GetCustomAttribute<DisplayAttribute>(); }
пример
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} \nDescription: {1}", metadata.Name, metadata.Description); }
выход
Name: Sea cruise Description: Cruising on a dinghy
вот код для получения информации из атрибута отображения. Он использует универсальный метод для извлечения атрибута. Если атрибут не найден, он преобразует значение enum в строку с регистром pascal / camel, преобразованным в регистр заголовка (полученный код здесь)
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(); } }и это метод расширения для строк для преобразования в регистр заголовка:
/// <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]))", " ")); }
получить словарь из перечисления.
public static IDictionary<string, int> ToDictionary(this Type enumType) { return Enum.GetValues(enumType) .Cast<object>() .ToDictionary(v => ((Enum)v).ToEnumDescription(), k => (int)k); }теперь назовем это как...
var dic = typeof(ActivityType).ToDictionary();EnumDecription Ext Метод
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 }
я реализовал этот метод расширения, чтобы получить описание от значений enum. Он работает для всех видов перечислений.
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(); } }
добавление моего решения для Net Framework и NetCore.
я использовал это для моей реализации Net Framework:
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 поэтому я изменил его, чтобы сделать это:
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"; } }Перечисление Пример:
public enum ExportTypes { [Display( Name = "csv", Description = "text/csv" )] CSV = 0 }пример использования для любого статического добавлено:
var myDescription = myEnum.Description();
воспользовавшись некоторые из новых функций языка C#, вы можете уменьшить количество строк:
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 отсутствует, он возвращается к перечислению.ToString ().
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; }
и если вы хотите получить полный список имен, вы можете сделать что-то вроде
typeof (PharmacyConfigurationKeys).GetFields() .Where(x => x.GetCustomAttributes(false).Any(y => typeof(DescriptionAttribute) == y.GetType())) .Select(x => ((DescriptionAttribute)x.GetCustomAttributes(false)[0]).Description);
Я этот ответ, чтобы настроить поле со списком из перечисления атрибутов, которые были велики.
затем мне нужно было закодировать обратное, чтобы я мог получить выбор из поля и вернуть перечисление в правильном типе.
Я также изменил код для обработки случая, когда атрибут отсутствовал
для пользы следующего человека, вот мое окончательное решение
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; } }}
в качестве альтернативы, вы можете сделать следующее:
Dictionary<FunkyAttributesEnum, string> description = new Dictionary<FunkyAttributesEnum, string>() { { FunkyAttributesEnum.NameWithoutSpaces1, "Name With Spaces1" }, { FunkyAttributesEnum.NameWithoutSpaces2, "Name With Spaces2" }, };и получить описание со следующим:
string s = description[FunkyAttributesEnum.NameWithoutSpaces1];на мой взгляд это более эффективный способ делать то, что вы хотите достичь, так как не нужны никакие размышления..
Ребята, если это поможет, я поделюсь с вами своим решением: Определение пользовательского атрибута:
[AttributeUsage(AttributeTargets.Field,AllowMultiple = false)] public class EnumDisplayName : Attribute { public string Name { get; private set; } public EnumDisplayName(string name) { Name = name; } }теперь, потому что мне это было нужно внутри определения HtmlHelper расширения HtmlHelper:
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"); } }надеюсь, что это помогает
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 "равно"
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() возвращает null, если displayname и enum name совпадают.....
вы также можете определить значение перечисления, например
Name_Without_Spaces, и когда вы хотите использовать описаниеName_Without_Spaces.ToString().Replace('_', ' ')заменить подчеркивания пробелами.
Comments