Uzyskiwanie atrybutów wartości Enum


483

Chciałbym wiedzieć, czy można uzyskać atrybuty enumwartości, a nie enumsamego? Załóżmy na przykład, że mam następujące elementy enum:

using System.ComponentModel; // for DescriptionAttribute

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

To, czego chcę, to typ wyliczenia, wykonanie 2 krotek wartości ciągu wyliczeniowego i jego opis.

Wartość była łatwa:

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

Ale jak uzyskać wartość atrybutu opisu, aby wypełnić Tuple.Desc? Mogę wymyślić, jak to zrobić, jeśli atrybut należy do enumsamego siebie, ale nie wiem, jak go zdobyć z wartości enum.




2
Przestrzeń nazw wymagana dla Opisu to System.ComponentModel
John M

Nie możesz także użyć System.ComponentModel i po prostu użyć własnego typu atrybutu; tak naprawdę nie ma w tym nic specjalnego DescriptionAttribute.
jrh

Odpowiedzi:


482

To powinno zrobić to, czego potrzebujesz.

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
Opcjonalnie użyj type.GetFields (BindingFlags.Public | BindingFlags.Static), aby uzyskać wszystkie memInfos naraz.
TrueWill

4
Musiałem przejść na typeof (FunkyAttributesEnum), ale poza tym działało dobrze. Dzięki.
Greg Randall

@AlexK Nie widzę, aby klasa Enum miała właściwość NameWithoutSpaces1. Skąd się bierze FunkyAttributesEnum.NameWithoutSpaces1?
Don

2
@Don, to nazwa członka enum z pytania OP.
UWAGI

287

Ten fragment kodu powinien dać ci miłą małą metodę rozszerzenia na dowolnym wyliczeniu, która pozwala ci pobrać ogólny atrybut. Uważam, że różni się od powyższej funkcji lambda, ponieważ jest prostszy w użyciu i nieco - wystarczy podać typ ogólny.

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
Użycie byłoby wtedy: string desc = myEnumVariable.GetAttributeOfType <DescriptionAttribute> () .Description;
Brad Rem

2
Podoba mi się to bardziej niż Scott, ponieważ użycie jest tutaj czystsze (mniej pisania), więc +1 :)
nawfal

3
Jeśli nie istnieje żaden atrybut, czy to nie rzuci IndexOutOfRangeException?
Erik Philips

6
lepiej użyć type.GetMember (Enum.GetName (type, enumVal)) dla memInfo, ponieważ enumVal.ToString () może nie być wiarygodny dla różnych lokalizacji.
Lin Song Yang

2
Jaki jest sens dzwonienia, GetCustomAttributes()a następnie zdobywania pierwszego elementu zamiast dzwonienia GetCustomAttribute()?
tigrou

81

Jest to ogólna implementacja wykorzystująca do wyboru lambda

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

Nazwij to tak:

string description = targetLevel.GetAttributeValue<DescriptionAttribute, string>(x => x.Description);

4
To jest świetne. Musimy tylko uważać, jeśli podana wartość wyliczenia jest kombinacją (dozwoloną przez FlagsAttribute). W takim przypadku enumeration.GetType().GetMember(enumeration.ToString())[0]zawiedzie.
remio

Najkrótszy, jaki mógłbyś napisać: value.GetType().GetField(value.ToString()).GetCustomAttributes(false).OfType<T>‌​().SingleOrDefault()ale musisz przyznać, że twój wyraźny sposób jest lepszy.
nawfal

2
Dodaję także publiczny statyczny ciąg GetDescription (to wyliczenie Enum) {return enumeration.GetAttributeValue <DescriptionAttribute, String> (x => x.Description); } w ten sposób jest to właśnie targetLevel.GetDescription ();
MarkKGreenway

65

Połączyłem tutaj kilka odpowiedzi, aby stworzyć nieco bardziej elastyczne rozwiązanie. Zapewniam to na wypadek, gdyby było to pomocne dla kogokolwiek innego w przyszłości. Oryginalny post tutaj .

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

}

To rozwiązanie tworzy parę metod rozszerzenia w Enum. Pierwszy pozwala użyć odbicia do pobrania dowolnego atrybutu związanego z twoją wartością. Drugi konkretnie wywołanie pobiera DescriptionAttributei zwraca goDescription wartość.

Jako przykład rozważ użycie DescriptionAttributeatrybutu zSystem.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
}

Aby użyć powyższej metody rozszerzenia, wystarczy teraz wywołać następujące polecenie:

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

lub

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

W ostatnim wierszu masz na myśli „atrybut.Description”? zwracany atrybut == null? value.ToString (): attribute.Description;
Jeson Martajaya

2
Uwielbiam to rozwiązanie, ale jest w nim błąd. Metoda GetAttribute zakłada, że ​​wartość wyliczona ma atrybut Opis, a zatem zgłasza wyjątek, gdy długość atrybutów wynosi 0. Zamień „atrybuty return (T) [0];” z „return (atrybuty. Długość> 0? (T) atrybuty [0]: null);”
Simon Gymer

@ SimonGymer dziękuję za sugestię - odpowiednio zaktualizowałem. :)
Troy Alford

38

Oprócz odpowiedzi AdamCrawford , stworzyłem bardziej wyspecjalizowane metody rozszerzenia, które dostarczają go, aby uzyskać opis.

public static string GetAttributeDescription(this Enum enumValue)
{
    var attribute = enumValue.GetAttributeOfType<DescriptionAttribute>();
    return attribute == null ? String.Empty : attribute.Description;
} 

stąd, aby uzyskać opis, możesz użyć oryginalnej metody rozszerzenia jako

string desc = myEnumVariable.GetAttributeOfType<DescriptionAttribute>().Description

lub możesz po prostu wywołać metodę rozszerzenia tutaj jako:

string desc = myEnumVariable.GetAttributeDescription();

Co, mam nadzieję, powinno uczynić twój kod nieco bardziej czytelnym.


16

Płynna jedna wkładka ...

Tutaj używam tego, DisplayAttributektóry zawiera zarówno właściwości Namei Description.

public static DisplayAttribute GetDisplayAttributesFrom(this Enum enumValue, Type enumType)
{
    return enumType.GetMember(enumValue.ToString())
                   .First()
                   .GetCustomAttribute<DisplayAttribute>();
}

Przykład

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

Wynik

Name: Sea cruise 
Description: Cruising on a dinghy

2
Ja też tego używam, to najczystsza ze wszystkich odpowiedzi! +1
Mafii,

To wydaje się całkiem przydatne! Thnx
Irf

7

Oto kod, aby uzyskać informacje z atrybutu Display. Korzysta z ogólnej metody pobierania atrybutu. Jeśli atrybut nie zostanie znaleziony, konwertuje wartość wyliczeniową na ciąg znaków z przypadkiem pascal / camel przekonwertowanym na tytuł (otrzymany tutaj kod )

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

A to jest metoda rozszerzenia ciągów do konwersji na wielkość liter:

    /// <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

Zaimplementowałem tę metodę rozszerzenia, aby uzyskać opis z wartości wyliczeniowych. Działa dla wszystkich rodzajów wyliczeń.

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

ogólna wersja tego samego rozwiązania jest już opublikowana. imo, lepiej.
nawfal

4

Pobierz słownik z enum.

public static IDictionary<string, int> ToDictionary(this Type enumType)
{
    return Enum.GetValues(enumType)
    .Cast<object>()
    .ToDictionary(v => ((Enum)v).ToEnumDescription(), k => (int)k); 
}

Teraz nazywaj to jak ...

var dic = typeof(ActivityType).ToDictionary();

Metoda rozszerzenia EnumDecription

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

Oto wersja .NET Core odpowiedzi AdamCrawford, wykorzystująca 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];
    }
}

Nie wierzę, że .NET Core (a raczej Standard teraz) ma GetMember, więc nie jestem pewien, jak to by działało.
Jeff

Jest w System.Reflection.TypeExtensions, poprawiłem swoją odpowiedź, aby to wymienić.
wonea

1
Gotcha, dzięki. Myślałem, że mogą istnieć pewne rozszerzenia.
Jeff

3

Dodanie mojego rozwiązania dla Net Framework i NetCore.

Użyłem tego do mojej implementacji 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";
    }
}

To nie działa dla NetCore, więc zmodyfikowałem go, aby to zrobić:

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

Przykład wyliczenia:

public enum ExportTypes
{
    [Display( Name = "csv", Description = "text/csv" )]
    CSV = 0
}

Przykładowe użycie dla dodanego statycznego:

var myDescription = myEnum.Description();

2

Korzystając z niektórych nowszych funkcji języka C #, możesz zmniejszyć liczbę wierszy:

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

Mam tę odpowiedź, aby skonfigurować pole kombi z atrybutów wyliczania, co było świetne.

Następnie musiałem zakodować odwrotną stronę, aby uzyskać wybór z pola i zwrócić wyliczenie we właściwym typie.

Zmodyfikowałem również kod, aby obsługiwał przypadek, w którym brakowało atrybutu

Dla korzyści następnej osoby, oto moje ostateczne rozwiązanie

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
Człowieku, widziałem tak wiele głupich i niewyjaśnionych rozwiązań, a twoje zabiło je. Dziękuję bardzo <3
Kadaj,

2

Jeśli enumzawiera wartość, na przykład Equalsmożesz napotkać kilka błędów przy użyciu rozszerzeń w wielu odpowiedziach tutaj. Wynika to z tego, że zwykle przyjmuje się, typeof(YourEnum).GetMember(YourEnum.Value)że zwróci tylko jedną wartość, która jest MemberInfotwoją enum. Oto nieco bezpieczniejsza wersja odpowiedź Adama Crawforda .

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

Ta metoda rozszerzenia uzyska ciąg reprezentujący wartość wyliczeniową za pomocą swojego XmlEnumAttribute. Jeśli nie ma XmlEnumAttribute, wraca do 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

A jeśli chcesz mieć pełną listę nazwisk, możesz zrobić coś takiego

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

0

Faceci, jeśli to pomoże, podzielę się z Wami moim rozwiązaniem: Definicja niestandardowego atrybutu:

    [AttributeUsage(AttributeTargets.Field,AllowMultiple = false)]
public class EnumDisplayName : Attribute
{
    public string Name { get; private set; }
    public EnumDisplayName(string name)
    {
        Name = name;
    }
}

Teraz, ponieważ potrzebowałem go w definicji HtmlHelper rozszerzenia 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");
    }
}

Mam nadzieję, że to pomoże


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             
    }

Teraz spowoduje błąd w tym przypadku 1 „równa się”

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

więc jeśli jest taka sama, zwracaj nazwę wyliczenia zamiast nazwy wyświetlanej, ponieważ enumMember.GetCustomAttribute () staje się pusty, jeśli nazwa wyświetlana i nazwa wyliczenia są takie same .....


0

Alternatywnie możesz wykonać następujące czynności:

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

W ten sposób rozwiązałem go bez użycia niestandardowych pomocników lub rozszerzeń w .NET core 3.1.

Klasa

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

Brzytwa

@using Enumerations

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

1
rozważ udzielenie odpowiedzi na pytanie, używając więcej niż tego, jak rozwiązałeś „to” - zacznij od uznania problemu i wyjaśnienia, w jaki sposób uważasz, że to „rozwiązuje”. Pamiętaj, że twoja odpowiedź może zostać wyjęta z kontekstu za kilka lat i byłaby wówczas prawie bezużyteczna. Dodanie do tego więcej, dodanie kontekstu
wyrównałoby

0

Wydajność ma znaczenie

Jeśli chcesz uzyskać lepszą wydajność, możesz to zrobić:

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

Dlaczego ma to lepszą wydajność?

Ponieważ wszystkie wbudowane metody używają kodu bardzo podobnego do tego, z wyjątkiem tego, że uruchamiają także wiele innych kodów, na których nam nie zależy . Kod Enum w C # jest ogólnie dość okropny.

Powyższy kod został opracowany przez Linq i usprawniony, więc zawiera tylko te bity, na których nam zależy.

Dlaczego wbudowany kod działa wolno?

Najpierw dotyczy Enum.ToString () -vs- Enum.GetName (..)

Zawsze używaj tego drugiego. (Lub jeszcze lepiej ani jedno, jak okaże się poniżej.)

ToString () używa tego ostatniego wewnętrznie, ale znowu robi też kilka innych rzeczy, których nie chcemy, np. Próbuje łączyć flagi, drukować liczby itp. Interesują nas tylko stałe zdefiniowane w wyliczeniu.

Enum.GetName z kolei pobiera wszystkie pola, tworzy tablicę ciągów dla wszystkich nazw, używa powyższego ToUInt64 na wszystkich swoich RawConstantValues ​​do utworzenia tablicy UInt64 wszystkich wartości, sortuje obie tablice zgodnie z wartością UInt64, a na końcu pobiera nazwę z tablicę nazw, wykonując BinarySearch w tablicy UInt64, aby znaleźć indeks żądanej wartości.

... a następnie wyrzucamy pola, a posortowane tablice używają tej nazwy, aby ponownie znaleźć pole.

Jedno słowo: „Ugh!”


-1

Alternatywnie możesz wykonać następujące czynności:

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

I uzyskaj opis z następującymi:

string s = description[FunkyAttributesEnum.NameWithoutSpaces1];

Moim zdaniem jest to bardziej efektywny sposób robienia tego, co chcesz osiągnąć, ponieważ nie ma potrzeby refleksji.


2
Jasne, ale odbicie nie jest tak złe, jak się wydaje.
Bryan Rowe

Nie mówię, że jest źle - używam go cały czas. Jest jednak często używany niepotrzebnie. :)
Ian P

44
To rozwiązanie odsuwa opis od samego wyliczenia, tworząc co najmniej dwa duże problemy. Po pierwsze, jeśli ktoś doda nową stałą wyliczania, będzie musiał wiedzieć, aby przejść do tego innego miejsca, aby dodać tam również wpis. Atrybuty są wyraźnym znakiem dla opiekuna tego, co muszą zrobić. Moim drugim problemem jest to, że to po prostu dużo więcej kodu. Atrybuty są zwarte.
scobi

1
@scott, ale pozwala ci określić własne zamówienie i wykluczyć wartości, których nie chcesz wyświetlać, co jest prawie zawsze tym, czego naprawdę chcę
Simon_Weaver

-2

Możesz także zdefiniować wartość wyliczenia, np. Name_Without_SpacesI kiedy chcesz użyć opisu, Name_Without_Spaces.ToString().Replace('_', ' ')aby zastąpić podkreślenia spacjami.


8
To bardzo nieeleganckie rozwiązanie. Rozważ skorzystanie z rozwiązania dostarczonego przez @Bryan
Johann
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.