Получение атрибутов значения Enum

Я хотел бы знать, можно ли получить атрибуты значений перечисления, а не самого перечисления? Например, предположим, что у меня есть следующее перечисление:

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

Но как мне получить значение атрибута описания, чтобы заполнить Tuple.Desc? Я могу подумать, как это сделать, если атрибут принадлежит самому перечислению, но я не понимаю, как получить его из значения перечисления.

Ответ 1

Это должно делать то, что вам нужно.

var enumType = typeof(FunkyAttributesEnum);
var memberInfos = enumType.GetMember(FunkyAttributesEnum.NameWithoutSpaces1.ToString());
var enumValueMemberInfo = memberInfos.FirstOrDefault(m => m.DeclaringType == enumType);
var valueAttributes = 
      enumValueMemberInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
var description = ((DescriptionAttribute)valueAttributes[0]).Description;

Ответ 2

Этот фрагмент кода должен дать вам небольшой метод расширения для любого перечисления, который позволяет получить общий атрибут. Я считаю, что это отличается от функции лямбда выше, потому что она проще в использовании и немного - вам нужно только передать общий тип.

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

Ответ 3

Это общая реализация с использованием лямбда для выбора

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

Ответ 4

Я объединил пару ответов здесь, чтобы создать немного более расширяемое решение. Я предоставляю это на всякий случай, если это пригодится кому-то еще в будущем. Оригинальное размещение здесь.

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

}

Это решение создает пару методов расширения в Enum. Первый позволяет использовать отражение, чтобы получить любой атрибут, связанный с вашим значением. Второй вызов вызывает извлечение DescriptionAttribute и возвращает ему значение Description.

В качестве примера рассмотрим использование атрибута DescriptionAttribute из System.ComponentModel

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
}

Чтобы использовать вышеупомянутый метод расширения, вы бы просто вызвали следующее:

Console.WriteLine(Days.Mon.ToName());

или же

var day = Days.Mon;
Console.WriteLine(day.ToName());

Ответ 5

В дополнение к ответу от 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();

Что, надеюсь, сделает ваш код более читабельным.

Ответ 6

Свободный один вкладыш...

Здесь я использую 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

Ответ 7

Вот код для получения информации из атрибута Display. Он использует общий метод для извлечения атрибута. Если атрибут не найден, он преобразует значение перечисления в строку с флагом 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)
    {
        ///questions/19498/net-how-can-you-split-a-caps-delimited-string-into-an-array/145002#145002
        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 "));
    }

Ответ 8

Получить словарь из перечисления.

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 Method

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
}

Ответ 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();
    }
}

Ответ 10

Здесь версия .NET Core для AdamCrawford, используя System.Reflection.TypeExtensions;

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

Ответ 11

Добавление моего решения для 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();

Ответ 12

Я отвечу, чтобы установить поле со списком из атрибутов enum, что было здорово.

Затем мне нужно было закодировать обратное, чтобы я мог получить выделение из поля и вернуть перечисление в правильный тип.

Я также модифицировал код для обработки случая, когда отсутствовал атрибут

Для преимуществ следующего человека, вот мое окончательное решение

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

}

Ответ 13

Воспользовавшись некоторыми новыми функциями языка С#, вы можете уменьшить количество строк:

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

Ответ 14

Этот метод расширения получит строковое представление значения перечисления, используя его XmlEnumAttribute. Если XmlEnumAttribute не присутствует, он возвращается к enum.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;
}

Ответ 15

И если вам нужен полный список имен, вы можете сделать что-то вроде

typeof (PharmacyConfigurationKeys).GetFields()
        .Where(x => x.GetCustomAttributes(false).Any(y => typeof(DescriptionAttribute) == y.GetType()))
        .Select(x => ((DescriptionAttribute)x.GetCustomAttributes(false)[0]).Description);

Ответ 16

Если ваше enum содержит значение типа Equals вы можете столкнуться с несколькими ошибками, используя некоторые расширения во многих ответах. Это потому, что обычно предполагается, что typeof(YourEnum).GetMember(YourEnum.Value) будет возвращать только одно значение, которое является MemberInfo вашего enum. Здесь чуть более безопасный вариант ответа Адама Кроуфорда.

public static class AttributeExtensions
{
    #region Methods

    public static T GetAttribute<T>(this Enum enumValue) where T : Attribute
    {
        var type = enumValue.GetType();
        var memberInfo = type.GetMember(enumValue.ToString());
        var member = memberInfo.FirstOrDefault(m => m.DeclaringType == type);
        var attribute = Attribute.GetCustomAttribute(member, typeof(T), false);
        return attribute is T ? (T)attribute : null;
    }

    #endregion
}

Ответ 17

Ребята, если это поможет, я поделюсь с вами своим решением: Определение пользовательского атрибута:

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

Надеюсь, что это поможет

Ответ 18

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

поэтому, если это одно и то же имя переименования возврата, а не отображаемое имя, потому что enumMember.GetCustomAttribute() получает значение null, если displayname и имя перечисления одинаковы.....

Ответ 19

В качестве альтернативы вы можете сделать следующее:

Dictionary<FunkyAttributesEnum, string> description = new Dictionary<FunkyAttributesEnum, string>()
    {
      { FunkyAttributesEnum.NameWithoutSpaces1, "Name With Spaces1" },
      { FunkyAttributesEnum.NameWithoutSpaces2, "Name With Spaces2" },
    };

И получите описание со следующим:

string s = description[FunkyAttributesEnum.NameWithoutSpaces1];

По-моему, это более эффективный способ делать то, что вы хотите достичь, поскольку не требуется никакого размышления.

Ответ 20

Вы также можете определить значение перечислимого типа, например Name_Without_Spaces, и если вы хотите, чтобы в описании использовалось Name_Without_Spaces.ToString().Replace('_', ' '), чтобы заменить символы подчеркивания пробелами.