Obtendo atributos do valor de Enum


483

Gostaria de saber se é possível obter atributos dos enumvalores e não do enumpróprio? Por exemplo, suponha que eu tenha o seguinte enum:

using System.ComponentModel; // for DescriptionAttribute

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

O que eu quero é dado o tipo de enumeração, produzir duas tuplas de valor de string de enumeração e sua descrição.

O valor foi fácil:

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

Mas como obtenho o valor do atributo description para preencher Tuple.Desc? Eu posso pensar em como fazê-lo se o Atributo pertencer a enumsi mesmo, mas não sei como obtê-lo a partir do valor de enum.




2
O espaço para nome necessário para Descrição é System.ComponentModel
John M

Você também pode simplesmente não usar System.ComponentModel e apenas usar seu próprio tipo de atributo; não há realmente nada de especial nisso DescriptionAttribute.
Jrh # 8/18

Respostas:


482

Isso deve fazer o que você precisa.

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
Opcionalmente, use type.GetFields (BindingFlags.Public | BindingFlags.Static) para obter todas as memInfos de uma só vez.
TrueWill 17/01

4
Eu tive que ir typeof (FunkyAttributesEnum), mas fora isso funcionou bem. Obrigado.
Greg Randall

@AlexK Não vejo a classe Enum ter uma propriedade NameWithoutSpaces1. De onde vem o FunkyAttributesEnum.NameWithoutSpaces1?
Don

2
@ Don, é o nome do membro enum da pergunta do OP.
MEMark

287

Esse trecho de código deve fornecer um ótimo método de extensão em qualquer enumeração que permita recuperar um atributo genérico. Acredito que seja diferente da função lambda acima, porque é mais simples de usar e um pouco - você só precisa passar no tipo genérico.

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
O uso seria então: string desc = myEnumVariable.GetAttributeOfType <DescriptionAttribute> () .Description;
Brad Rem

2
I como este mais do que Scott, porque o uso é mais limpo aqui (menos digitação), de modo +1 :)
Nawfal

3
Se nenhum atributo existir, isso não geraria um IndexOutOfRangeException?
Erik Philips

6
use melhor type.GetMember (Enum.GetName (type, enumVal)) para o memInfo como enumVal.ToString () pode não ser confiável para diferentes localidades.
Lin Song Yang

2
Qual é o sentido de chamar GetCustomAttributes()e obter o primeiro elemento em vez de chamar GetCustomAttribute()?
tigrou 24/02

81

Esta é uma implementação genérica usando um lambda para a seleção

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

Chame assim:

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

4
Isso é ótimo. Só precisamos ter cuidado se o valor de enumeração fornecido for uma combinação (permitida por FlagsAttribute). Nesse caso, enumeration.GetType().GetMember(enumeration.ToString())[0]falhará.
Remio

O mais curto que você pode escrever:, value.GetType().GetField(value.ToString()).GetCustomAttributes(false).OfType<T>‌​().SingleOrDefault()mas tem que admitir que seu caminho explícito é melhor.
Nawfal

2
Também adiciono String estática pública GetDescription (essa enumeração Enum) {return enumeration.GetAttributeValue <DescriptionAttribute, String> (x => x.Description); } dessa forma, é apenas targetLevel.GetDescription ();
MarkKGreenway

65

Mesclamos algumas respostas aqui para criar uma solução um pouco mais extensível. Estou fornecendo apenas no caso de ser útil para mais alguém no futuro. Postagem original aqui .

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

}

Esta solução cria um par de métodos de extensão no Enum. O primeiro permite que você use a reflexão para recuperar qualquer atributo associado ao seu valor. A segunda chamada especificamente recupera DescriptionAttributee retorna seu Descriptionvalor.

Como exemplo, considere usar o DescriptionAttributeatributo deSystem.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
}

Para usar o método de extensão acima, basta chamar o seguinte:

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

ou

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

Na última linha, você quer dizer "attribute.Description"? atributo de retorno == null? value.ToString (): attribute.Description;
Jeson Martajaya

2
Eu amo essa solução, mas há um bug nela. O método GetAttribute assume que o valor de enum possui um atributo Description e, portanto, gera uma exceção quando o comprimento dos atributos for 0. Substitua os "return (T) attribute [0];" com "return (attribute.Length> 0? (T) attribute [0]: null);"
Simon Gymer

@SimonGymer obrigado pela sugestão - atualizei de acordo. :)
Troy Alford

38

Além da resposta do AdamCrawford , eu criei ainda métodos de extensão mais especializados que o alimentam para obter a descrição.

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

portanto, para obter a descrição, você pode usar o método de extensão original como

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

ou você pode simplesmente chamar o método de extensão aqui como:

string desc = myEnumVariable.GetAttributeDescription();

O que deve tornar seu código um pouco mais legível.


16

Um forro fluente ...

Aqui eu estou usando o DisplayAttributeque contém as propriedades Namee Description.

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

Exemplo

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

Resultado

Name: Sea cruise 
Description: Cruising on a dinghy

2
Eu também uso isso, é a mais limpa de todas as respostas! +1
Mafii

Isso parece ser bastante útil! Thnx
Irf 28/02/19

7

Aqui está o código para obter informações de um atributo Display. Ele usa um método genérico para recuperar o atributo. Se o atributo não for encontrado, ele converterá o valor de enum em uma string com pascal / camel case convertido em title case (código obtido aqui )

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

E este é o método de extensão para seqüências de caracteres para converter em maiúsculas e minúsculas:

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

Eu implementei esse método de extensão para obter a descrição dos valores de enumeração. Funciona para todos os tipos de enumerações.

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

versão genérica da mesma solução já foi postada. imo, melhor.
Nawfal

4

Obtenha o dicionário de enum.

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

Agora chame isso como ...

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

Método Ext 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

Aqui está a versão do .NET Core da resposta de AdamCrawford, usando 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];
    }
}

Não acredito que o .NET Core (ou melhor, o Standard agora) tenha o GetMember, então não tenho certeza de como isso funcionaria.
25416 Jeff

Está em System.Reflection.TypeExtensions, revisei minha resposta para listar isso.
wonea

1
Entendi, obrigado. Eu pensei que poderia haver algumas extensões em jogo.
26416 Jeff

3

Adicionando minha solução para o Net Framework e o NetCore.

Eu usei isso para minha implementação do 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";
    }
}

Isso não funciona para o NetCore, então eu o modifiquei para fazer isso:

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

Exemplo de enumeração:

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

Uso de amostra para estática adicionada:

var myDescription = myEnum.Description();

2

Aproveitando alguns dos recursos mais recentes da linguagem C #, você pode reduzir a contagem de linhas:

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

Eu esta resposta para configurar uma caixa de combinação de um enum atributos que foi ótimo.

Em seguida, precisei codificar o inverso para que eu pudesse obter a seleção da caixa e retornar a enumeração no tipo correto.

Também modifiquei o código para lidar com o caso em que um atributo estava ausente

Para os benefícios da próxima pessoa, aqui está minha solução final

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
Cara, eu já vi tantas soluções estúpidas e inexplicáveis, e a sua a matou. Muito obrigado <3
Kadaj

2

Se o seu enumcontém um valor como Equalsvocê pode encontrar alguns erros usando algumas extensões em várias respostas aqui. Isso ocorre porque normalmente é assumido que typeof(YourEnum).GetMember(YourEnum.Value)retornaria apenas um valor, que é o MemberInfoseu enum. Aqui está uma versão um pouco mais segura da resposta de Adam Crawford .

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

Esse método de extensão obterá uma representação de seqüência de caracteres de um valor de enum usando seu XmlEnumAttribute. Se nenhum XmlEnumAttribute estiver presente, ele voltará a 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

E se você quiser a lista completa de nomes, pode fazer algo como

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

0

Pessoal, se ajudar, vou compartilhar com vocês minha solução: Definição do atributo Custom:

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

Agora, porque eu precisava dele dentro da definição HtmlHelper da extensão 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");
    }
}

Espero que ajude


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             
    }

Agora ele produzirá erro neste caso 1 "Igual a"

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

portanto, se for o mesmo nome de enum de retorno, em vez de nome de exibição, porque enumMember.GetCustomAttribute () será nulo se displayname e nome de enum forem iguais .....


0

Como alternativa, você pode fazer o seguinte:

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

Foi assim que resolvi isso sem usar extensões ou assistentes personalizados com o .NET core 3.1.

Classe

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

Navalha

@using Enumerations

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

1
considere responder à pergunta usando mais do que como você o resolveu - comece reconhecendo o problema e explicando como você acha que isso resolve o problema. Lembre-se de que sua resposta poderá ser retirada de contexto daqui a alguns anos e seria quase inútil. Adicionando mais, adicionar algum contexto aumentaria sua resposta e sua possível relevância histórica / arquivística
OldFart 14/02

0

O desempenho importa

Se você quer um melhor desempenho, este é o caminho a seguir:

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

Por que isso tem melhor desempenho?

Como todos os métodos internos usam código muito semelhante a esse, exceto que também executam vários outros códigos com os quais não nos importamos . O código Enum do C # é bastante horrível em geral.

O código acima foi Linq-ified e simplificado, portanto, ele contém apenas os bits com os quais nos preocupamos.

Por que o código interno é lento?

Primeiro sobre Enum.ToString () -vs- Enum.GetName (..)

Sempre use o último. (Ou melhor ainda, como ficará claro abaixo.)

ToString () usa o último internamente, mas, novamente, também faz um monte de outras coisas que não queremos, por exemplo, tenta combinar sinalizadores, imprimir números etc. Estamos interessados ​​apenas nas constantes definidas dentro do enum.

Por sua vez, Enum.GetName obtém todos os campos, cria uma matriz de string para todos os nomes, usa o ToUInt64 acima em todos os seus RawConstantValues ​​para criar uma matriz UInt64 de todos os valores, classifica as duas matrizes de acordo com o valor UInt64 e, finalmente, obtém o nome de a matriz de nomes fazendo um BinarySearch na matriz UInt64 para encontrar o índice do valor que desejávamos.

... e depois jogamos os campos e as matrizes classificadas para longe, use esse nome para encontrar o campo novamente.

Uma palavra: "Ugh!"


-1

Como alternativa, você pode fazer o seguinte:

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

E obtenha a descrição com o seguinte:

string s = description[FunkyAttributesEnum.NameWithoutSpaces1];

Na minha opinião, esta é uma maneira mais eficiente de fazer o que você deseja realizar, pois nenhuma reflexão é necessária.


2
Claro, mas a reflexão não é tão ruim quanto as pessoas imaginam.
Bryan Rowe

Não estou dizendo que é ruim - eu uso o tempo todo. É frequentemente usado desnecessariamente, no entanto. :)
Ian P

44
Essa solução afasta a descrição do próprio enum, criando pelo menos dois grandes problemas. Primeiro, se alguém adicionar uma nova constante de enumeração, precisará saber para ir para outro local para adicionar uma entrada também. Os atributos são um sinal claro para o mantenedor do que eles precisam fazer. Meu segundo problema é que é apenas muito mais código. Os atributos são compactos.
Scobi

1
@ Scott, mas ele permite que você especifique seu próprio fim, e excluir valores que você não deseja exibir o que é quase sempre o que eu realmente quero
Simon_Weaver

-2

Você também pode definir um valor de enumeração como Name_Without_Spacese, quando desejar, use uma descrição Name_Without_Spaces.ToString().Replace('_', ' ')para substituir os sublinhados por espaços.


8
Esta é uma solução muito deselegante. Considere usar a solução fornecida por @Bryan
Johann
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.