Отримання атрибутів значення 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);

Але як я можу отримати значення атрибута опису для заповнення Tuple.Desc? Я можу подумати, як це зробити, якщо атрибут належить самому enumсобі, але я в збитті щодо того, як отримати його від значення значень enum.


З іншого питання stackoverflow.com/questions/469287 / ...
finnw


2
Для опису потрібен простір імен System.ComponentModel
Джон М

Ви також можете просто не використовувати System.ComponentModel, а просто використовувати власний тип атрибутів; насправді нічого такого особливого немає DescriptionAttribute.
jrh

Plesae дивіться по цьому посиланню: stackoverflow.com/a/58954215/5576498
AminGolmahalle

Відповіді:


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
@Don, це ім'я члена переліку з питання ОП.
ПАМ’ЯТ

287

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

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> (). Опис;
Бред Рем

2
Мені це подобається більше, ніж Скотту, тому що використання тут чистіше (менше набирати
текстів

3
Якщо жодного атрибута не існує, чи не буде це кидком IndexOutOfRangeException?
Ерік Філіпс

6
краще використовувати type.GetMember (Enum.GetName (тип, 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]не вдасться.
remio

Найкоротший, про який ти міг написати:, 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());

В останньому рядку ви маєте на увазі "атрибут. Опис"? return attribute == null? value.ToString (): attribute.Description;
Jeson Martajaya

2
Я люблю це рішення, але в ньому є помилка. Метод GetAttribute передбачає, що значення enum має атрибут Description, і таким чином викидає виняток, коли довжина атрибутів дорівнює 0. Замініть атрибути return (T) [0]; " з "return (attributes.Length> 0? (T) атрибути [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. Для отримання атрибуту використовується загальний метод. Якщо атрибут не знайдено, він перетворює значення 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]))", "$1 "));
    }

4

Я реалізував цей метод розширення, щоб отримати опис із значень 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();
    }
}

загальна версія того ж рішення вже розміщена. imo, краще.
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

Я цю відповідь налаштувати комбіновану скриньку з атрибутів 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;
    }
}

}


1
Людина, я бачив стільки дурних і незрозумілих рішень, і твій це вбив. Дякую велике <3
Kadaj

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

Цей метод розширення отримає рядкове подання значення enum, використовуючи його 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;
    }

тож якщо це те саме ім'я enum return, а не відображуване ім'я, тому що enumMember.GetCustomAttribute () стає нульовим, якщо ім'я відображення та enum однакові .....


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
Подумайте, як відповісти на питання, використовуючи більше, ніж те, як ви вирішили "це" - почніть з визнання проблеми та пояснення, як ви думаєте, це вирішує "це". Пам'ятайте, що ваша відповідь може бути виведена з контексту через кілька років, і тоді вона буде майже марною. Додавши до нього більше, додавши деякий контекст, вирівнюватиме вашу відповідь та її можливу історичну / архівну актуальність
OldFart

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 та впорядкований, тому він містить лише біти, про які нам важливо.

Чому вбудований код повільний?

Спочатку про Enum.ToString () -vs- Enum.GetName (..)

Завжди використовуйте останнє. (А ще краще ні те, як стане зрозуміло нижче.)

ToString () використовує останнє внутрішньо, але знову ж таки, також робить купу інших речей, які ми не хочемо, наприклад, намагається поєднувати прапори, роздрукувати числа тощо. Нас цікавлять лише константи, визначені всередині enum.

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
Звичайно, але рефлексія не так вже й погана, як це роблять люди.
Брайан Роу

Не кажучи, що це погано - я ним користуюся постійно. Однак він часто використовується без потреби. :)
Ян P

44
Це рішення відсуває опис від самої перетворення, створюючи принаймні дві великі проблеми. По-перше, якщо хтось додасть нову константу перерахунку, він повинен знати, щоб перейти до цього іншого місця, щоб також додати запис. Атрибути - це чіткий знак для того, хто підтримує, що їм потрібно робити. Моя друга проблема з цим полягає в тому, що це просто набагато більше коду. Атрибути компактні.
Скобі

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

-2

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


8
Це дуже неелегантне рішення. Подумайте про використання рішення, наданого @Bryan
Johann
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.