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


483

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

using System.ComponentModel; // for DescriptionAttribute

enum FunkyAttributesEnum
{
    [Description("Name With Spaces1")]
    NameWithoutSpaces1,    
    [Description("Name With Spaces2")]
    NameWithoutSpaces2
}

То, что я хочу, это дать тип enum, произвести 2 кортежа значения строки enum и его описание.

Значение было легко:

Array values = System.Enum.GetValues(typeof(FunkyAttributesEnum));
foreach (int value in values)
    Tuple.Value = Enum.GetName(typeof(FunkyAttributesEnum), value);

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


Из другого вопроса stackoverflow.com/questions/469287/…
finnw


2
пространство имен, необходимое для описания, - System.ComponentModel
Джон М

Вы также можете просто не использовать System.ComponentModel и просто использовать свой собственный тип атрибута; в этом нет ничего особенного DescriptionAttribute.
JRH

Ответы:


482

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

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;

10
При желании используйте type.GetFields (BindingFlags.Public | BindingFlags.Static), чтобы получить все memInfos одновременно.
TrueWill

4
Я должен был пойти typeof (FunkyAttributesEnum), но в остальном он работал хорошо. Спасибо.
Грег Рэндалл

@AlexK Я не вижу, чтобы у класса Enum было свойство NameWithoutSpaces1. Откуда берется FunkyAttributesEnum.NameWithoutSpaces1?
Дон

2
@ Дон, это имя члена enum из вопроса ОП.
MEMark

288

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

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><![CDATA[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;
    }
}

19
Тогда использование будет следующим: string desc = myEnumVariable.GetAttributeOfType <DescriptionAttribute> (). Description;
Брэд Рем

2
Мне это нравится больше, чем у Скотта, потому что здесь использование чище (меньше печатания), поэтому +1 :)
nawfal

3
Если атрибут не существует, разве это не бросит IndexOutOfRangeException?
Эрик Филипс

6
лучше использовать type.GetMember (Enum.GetName (type, enumVal)) для memInfo, так как enumVal.ToString () может быть ненадежным для разных локалей.
Лин Сонг Ян

2
Какой смысл звонить, GetCustomAttributes()тогда получить первый элемент вместо звонка GetCustomAttribute()?
Tigrou

81

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

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
Это замечательно. Мы просто должны быть осторожны, если заданное значение перечисления является комбинацией (разрешено FlagsAttribute). В этом случае enumeration.GetType().GetMember(enumeration.ToString())[0]не получится.
ремио

Самое короткое, что вы можете написать:, value.GetType().GetField(value.ToString()).GetCustomAttributes(false).OfType<T>‌​().SingleOrDefault()но лучше признать, что ваш явный путь лучше.
nawfal

2
Я также добавляю открытую статическую String GetDescription (это перечисление Enum) {return enumeration.GetAttributeValue <DescriptionAttribute, String> (x => x.Description); } таким образом, это просто targetLevel.GetDescription ();
MarkKGreenway

65

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

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

В последней строке вы имеете в виду «attribute.Description»? возвращаемый атрибут == ноль? value.ToString (): attribute.Description;
Джесон Мартаджая

2
Мне нравится это решение, но в нем есть ошибка. Метод GetAttribute предполагает, что значение перечисления имеет атрибут Description, и поэтому выдает исключение, когда длина атрибутов равна 0. Замените «return (T) attribute [0];» с "return (attribute.Length> 0? (T) attribute [0]: null);"
Саймон Гимер,

@SimonGymer спасибо за предложение - я обновил соответственно. :)
Трой Алфорд

38

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

Который, надеюсь, должен сделать ваш код немного более читабельным.


16

Свободный один лайнер ...

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

2
Я тоже этим пользуюсь, это самый чистый из всех ответов! +1
Мафия

Это кажется весьма полезным! Thnx
IRF

7

Вот код для получения информации из атрибута Display. Он использует универсальный метод для получения атрибута. Если атрибут не найден, он преобразует значение перечисления в строку с регистром паскаля / верблюда, преобразованным в регистр заголовка (код, полученный здесь )

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]))", "$1 "));
    }

4

Я реализовал этот метод расширения, чтобы получить описание из значений перечисления. Это работает для всех видов перечислений.

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

Общая версия того же решения уже опубликована. ИМХО, лучше.
Nawfal

4

Получить словарь из enum.

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
}

3

Вот версия .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];
    }
}

Я не верю, что .NET Core (точнее, сейчас Standard) имеет GetMember, поэтому я не уверен, как это будет работать.
Джефф

Это в System.Reflection.TypeExtensions, я пересмотрел свой ответ, чтобы перечислить это.
Wonea

1
Понятно, спасибо. Я думал, что могут быть некоторые расширения в игре.
Джефф

3

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

2

Используя некоторые из новых возможностей языка 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();

2

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

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

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

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

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
Чувак, я видел столько глупых и необъяснимых решений, а твое убило его. Большое спасибо <3
Кадаж

2

Если ваш 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
}

1

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

1

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

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

0

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

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

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


0
    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, если отображаемое имя и имя перечисления совпадают .....


0

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

List<SelectListItem> selectListItems = new List<SelectListItem>();

    foreach (var item in typeof(PaymentTerm).GetEnumValues())
    {
        var type = item.GetType();
        var name = type.GetField(item.ToString()).GetCustomAttributesData().FirstOrDefault()?.NamedArguments.FirstOrDefault().TypedValue.Value.ToString();
        selectListItems.Add(new SelectListItem(name, type.Name));

    }

0

Вот как я решил это без использования пользовательских помощников или расширений с .NET core 3.1.

Учебный класс

public enum YourEnum
{
    [Display(Name = "Suryoye means Arameans")]
    SURYOYE = 0,
    [Display(Name = "Oromoye means Syriacs")]
    OROMOYE = 1,
}

бритва

@using Enumerations

foreach (var name in Html.GetEnumSelectList(typeof(YourEnum)))
{
    <h1>@name.Text</h1>
}

1
подумайте над ответом на вопрос, используя не только то, как вы решили «это», - начните с признания проблемы и объяснения того, как, по вашему мнению, это решает «это». Помните, что ваш ответ может быть вырван из контекста через несколько лет, и тогда он будет почти бесполезен. Добавление большего к нему, добавление некоторого контекста
повысит

0

Вопросы производительности

Если вы хотите лучшую производительность, вот путь:

public static class AdvancedEnumExtensions
{
    /// <summary>
    /// Gets the custom attribute <typeparamref name="T"/> for the enum constant, if such a constant is defined and has such an attribute; otherwise null.
    /// </summary>
    public static T GetCustomAttribute<T>(this Enum value) where T : Attribute
    {
        return GetField(value)?.GetCustomAttribute<T>(inherit: false);
    }

    /// <summary>
    /// Gets the FieldInfo for the enum constant, if such a constant is defined; otherwise null.
    /// </summary>
    public static FieldInfo GetField(this Enum value)
    {
        ulong u64 = ToUInt64(value);
        return value
            .GetType()
            .GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)
            .Where(f => ToUInt64(f.GetRawConstantValue()) == u64)
            .FirstOrDefault();
    }

    /// <summary>
    /// Checks if an enum constant is defined for this enum value
    /// </summary>
    public static bool IsDefined(this Enum value)
    {
        return GetField(value) != null;
    }

    /// <summary>
    /// Converts the enum value to UInt64
    /// </summary>
    public static ulong ToUInt64(this Enum value) => ToUInt64((object)value);

    private static ulong ToUInt64(object value)
    {
        switch (Convert.GetTypeCode(value))
        {
            case TypeCode.SByte:
            case TypeCode.Int16:
            case TypeCode.Int32:
            case TypeCode.Int64:
                return unchecked((ulong)Convert.ToInt64(value, CultureInfo.InvariantCulture));

            case TypeCode.Byte:
            case TypeCode.UInt16:
            case TypeCode.UInt32:
            case TypeCode.UInt64:
            case TypeCode.Char:
            case TypeCode.Boolean:
                return Convert.ToUInt64(value, CultureInfo.InvariantCulture);

            default: throw new InvalidOperationException("UnknownEnumType");
        }
    }
}

Почему это имеет лучшую производительность?

Поскольку все встроенные методы используют код, очень похожий на этот, за исключением того, что они также запускают кучу другого кода, который нас не волнует . Код Enum в C # вообще ужасен.

Приведенный выше код был Linq-ified и оптимизирован, поэтому он содержит только те биты, которые нам нужны.

Почему встроенный код работает медленно?

Сначала о Enum.ToString () -vs- Enum.GetName (..)

Всегда используйте последнее. (Или, еще лучше, ни то, ни другое, как станет ясно ниже.)

ToString () использует последнее внутренне, но, опять же, также делает кучу других вещей, которые нам не нужны, например, пытается объединить флаги, распечатать числа и т. Д. Нас интересуют только константы, определенные внутри перечисления.

Enum.GetName, в свою очередь, получает все поля, создает строковый массив для всех имен, использует вышеупомянутый ToUInt64 на всех их RawConstantValues ​​для создания массива всех значений UInt64, сортирует оба массива в соответствии со значением UInt64 и, наконец, получает имя из массив имен, выполнив BinarySearch в массиве UInt64, чтобы найти индекс нужного нам значения.

... а затем мы выбрасываем поля и отсортированные массивы, используя это имя, чтобы снова найти поле.

Одно слово: "Тьфу!"


-1

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

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

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

string s = description[FunkyAttributesEnum.NameWithoutSpaces1];

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


2
Конечно, но размышления не так плохи, как это делают люди.
Брайан Роу

Не говорю, что это плохо - я использую это все время. Это часто используется без необходимости, хотя. :)
Ян П

44
Это решение отодвигает описание от самого перечисления, создавая как минимум две большие проблемы. Во-первых, если кто-то добавляет новую константу перечисления, ему нужно знать, чтобы перейти в это другое место и добавить туда запись. Атрибуты являются четким знаком для сопровождающего того, что им нужно сделать. Моя вторая проблема в том, что это просто намного больше кода. Атрибуты компактны.
scobi

1
@scott, но он позволяет вам указать свой собственный порядок и исключить значения, которые вы не хотите отображать, что почти всегда то, что я на самом деле хочу
Simon_Weaver

-2

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


8
Это очень не элегантное решение. Подумайте об использовании решения, предоставленного @Bryan
Johann
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.