Obteniendo atributos del valor de Enum


483

Me gustaría saber si es posible obtener atributos de los enumvalores y no de los enummismos. Por ejemplo, supongamos que tengo lo siguiente enum:

using System.ComponentModel; // for DescriptionAttribute

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

Lo que quiero es el tipo de enumeración, producir 2 tuplas de valor de cadena de enumeración y su descripción.

El valor fue fácil:

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

Pero, ¿cómo obtengo el valor del atributo de descripción para completar Tuple.Desc? Puedo pensar en cómo hacerlo si el atributo pertenece a enumsí mismo, pero no sé cómo obtenerlo del valor de enum.




2
el espacio de nombres requerido para la Descripción es System.ComponentModel
John M

También puede simplemente no usar System.ComponentModel y simplemente usar su propio tipo de atributo; Realmente no hay nada tan especial DescriptionAttribute.
jrh

Respuestas:


482

Esto debería hacer lo que necesitas.

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 obtener todos los memInfos a la vez.
TrueWill

44
Tuve que ir typeof (FunkyAttributesEnum), pero aparte de eso funcionó bien. Gracias.
Greg Randall

@AlexK No veo que la clase Enum tenga una propiedad NameWithoutSpaces1. ¿De dónde viene el FunkyAttributesEnum.NameWithoutSpaces1?
Don

2
@ Don, es el nombre del miembro enum de la pregunta del OP.
MEMark

288

Este fragmento de código debería proporcionarle un pequeño método de extensión en cualquier enumeración que le permita recuperar un atributo genérico. Creo que es diferente a la función lambda anterior porque es más simple de usar y ligeramente, solo necesita pasar el 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
El uso sería: string desc = myEnumVariable.GetAttributeOfType <DescriptionAttribute> () .Description;
Brad Rem

2
Este me gusta más que el de Scott, porque el uso es más limpio aquí (menos escribir), así que +1 :)
nawfal

3
Si no existe ningún atributo, ¿no arrojaría un IndexOutOfRangeException?
Erik Philips

66
mejor use type.GetMember (Enum.GetName (type, enumVal)) para memInfo ya que enumVal.ToString () puede no ser confiable para diferentes configuraciones regionales.
Lin Song Yang

2
¿Cuál es el punto de llamar y GetCustomAttributes()luego obtener el primer elemento en lugar de llamar GetCustomAttribute()?
tigrou

81

Esta es una implementación genérica que usa una lambda para la selección

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

Llámalo así:

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

44
Esto es genial. Solo debemos tener cuidado si el valor de enumeración dado es una combinación (permitido por FlagsAttribute). En este caso, enumeration.GetType().GetMember(enumeration.ToString())[0]fallará.
remio

Lo más corto que podrías escribir: value.GetType().GetField(value.ToString()).GetCustomAttributes(false).OfType<T>‌​().SingleOrDefault()pero tienes que admitir que tu forma explícita es mejor.
nawfal

2
También agrego String GetDescription estático público (esta enumeración Enum) {return enumeration.GetAttributeValue <DescriptionAttribute, String> (x => x.Description); } de esa manera es solo targetLevel.GetDescription ();
MarkKGreenway

65

He fusionado algunas de las respuestas aquí para crear una solución un poco más extensible. Lo estoy proporcionando en caso de que sea útil para alguien más en el futuro. Publicación original aquí .

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 solución crea un par de métodos de extensión en Enum. El primero le permite usar la reflexión para recuperar cualquier atributo asociado con su valor. La segunda llamada específicamente recupera DescriptionAttributey devuelve su Descriptionvalor.

Como ejemplo, considere usar el 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 el método de extensión anterior, simplemente debería llamar a lo siguiente:

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

o

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

En la última línea, te refieres a "atributo. Descripción"? atributo de retorno == nulo? value.ToString (): atributo.Descripción;
Jeson Martajaya

2
Me encanta esta solución, pero hay un error en ella. El método GetAttribute supone que el valor de enumeración tiene un atributo Descripción y, por lo tanto, genera una excepción cuando la longitud de los atributos es 0. Reemplace los "atributos de retorno (T) [0];" con "return (atributos. Longitud> 0? (T) atributos [0]: nulo);"
Simon Gymer

@SimonGymer gracias por la sugerencia: he actualizado en consecuencia. :)
Troy Alford

38

Además de la respuesta de AdamCrawford , he creado métodos de extensión más especializados que se alimentan de él para obtener la descripción.

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

por lo tanto, para obtener la descripción, puede usar el método de extensión original como

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

o simplemente puede llamar al método de extensión aquí como:

string desc = myEnumVariable.GetAttributeDescription();

Lo que debería hacer que su código sea un poco más legible.


16

Un forro fluido ...

Aquí estoy usando el DisplayAttributeque contiene las propiedades Namey Description.

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

Ejemplo

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

Salida

Name: Sea cruise 
Description: Cruising on a dinghy

2
Yo también uso esto, ¡es la más limpia de todas las respuestas! +1
Mafii

¡Esto parece ser bastante útil! Thnx
Irf

7

Aquí hay un código para obtener información de un atributo Display. Utiliza un método genérico para recuperar el atributo. Si no se encuentra el atributo, convierte el valor de enumeración en una cadena con el caso pascal / camel convertido al caso del título (código obtenido aquí )

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

Y este es el método de extensión para cadenas para convertir a mayúsculas y 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

Implementé este método de extensión para obtener la descripción de los valores de enumeración. Funciona para todo tipo de enumeraciones.

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

La versión genérica de la misma solución ya está publicada. OMI, mejor.
nawfal

4

Obtenga el diccionario 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); 
}

Ahora llama a esto como ...

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

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

Aquí está la versión .NET Core de la respuesta 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];
    }
}

No creo que .NET Core (o más bien, Standard ahora) tenga GetMember, así que no estoy seguro de cómo funcionaría.
Jeff

Está en System.Reflection.TypeExtensions, he revisado mi respuesta para enumerar esto.
wonea

1
Te tengo, gracias. Pensé que podría haber algunas extensiones en juego.
Jeff

3

Agregar mi solución para Net Framework y NetCore.

Utilicé esto para mi implementación de 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";
    }
}

Esto no funciona para NetCore, así que lo modifiqué para hacer esto:

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

Ejemplo de enumeración:

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

Uso de muestra para cualquier estática agregada:

var myDescription = myEnum.Description();

2

Aprovechando algunas de las características más nuevas del lenguaje C #, puede reducir el recuento de líneas:

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

En esta respuesta, configuré un cuadro combinado a partir de los atributos de una enumeración que fue genial.

Luego necesitaba codificar el reverso para poder obtener la selección del cuadro y devolver la enumeración en el tipo correcto.

También modifiqué el código para manejar el caso donde faltaba un atributo

Para los beneficios de la próxima persona, aquí está mi solución 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
Hombre, he visto tantas soluciones estúpidas e inexplicables, y la tuya lo mató. Muchas gracias <3
Kadaj

2

Si enumcontiene un valor como el Equalsque podría encontrar algunos errores usando algunas extensiones en muchas respuestas aquí. Esto se debe a que normalmente se supone que typeof(YourEnum).GetMember(YourEnum.Value)devolvería solo un valor, que es el MemberInfode su enum. Aquí hay una versión un poco más segura de la respuesta 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

Este método de extensión obtendrá una representación de cadena de un valor enum utilizando su XmlEnumAttribute. Si no hay XmlEnumAttribute presente, recurre 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

Y si quieres la lista completa de nombres, puedes hacer 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

Chicos, si ayuda, compartiré con ustedes mi solución: Definición del atributo personalizado:

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

Ahora porque lo necesitaba dentro de la definición HtmlHelper de la extensión 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 eso ayude


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             
    }

Ahora producirá un error en este caso 1 "Igual"

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

así que si es el mismo nombre de enumeración de retorno en lugar de mostrar el nombre porque enumMember.GetCustomAttribute () se vuelve nulo si el nombre de visualización y el nombre de enumeración son iguales .....


0

Alternativamente, puede hacer lo siguiente:

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

Así es como lo resolví sin usar ayudantes personalizados o extensiones con .NET core 3.1.

Clase

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

Maquinilla de afeitar

@using Enumerations

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

1
considere responder a la pregunta usando más de cómo lo resolvió: comience por reconocer el problema y explique cómo cree que esto lo resuelve. Recuerde que su respuesta podría estar fuera de contexto en algunos años a partir de ahora y sería casi inútil. Agregar más, agregar un poco de contexto
elevaría

0

El rendimiento importa

Si desea un mejor rendimiento, este es el camino 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 qué tiene esto un mejor rendimiento?

Debido a que todos los métodos incorporados usan código muy similar a este, excepto que también ejecutan un montón de otro código que no nos importa . El código Enum de C # es bastante horrible en general.

El código anterior se ha simplificado y simplificado para que solo contenga los bits que nos interesan.

¿Por qué el código incorporado es lento?

Primero con respecto a Enum.ToString () -vs- Enum.GetName (..)

Siempre usa este último. (O mejor aún, tampoco, como se verá más adelante).

ToString () usa este último internamente, pero de nuevo, también hace un montón de otras cosas que no queremos, por ejemplo, intenta combinar banderas, imprimir números, etc. Solo nos interesan las constantes definidas dentro de la enumeración.

Enum.GetName a su vez obtiene todos los campos, crea una matriz de cadenas para todos los nombres, usa el ToUInt64 anterior en todos sus RawConstantValues ​​para crear una matriz UInt64 de todos los valores, clasifica ambas matrices de acuerdo con el valor UInt64 y finalmente obtiene el nombre de la matriz de nombres haciendo una BinarySearch en la matriz UInt64 para encontrar el índice del valor que queríamos.

... y luego tiramos los campos y las matrices ordenadas usan ese nombre para encontrar el campo nuevamente.

Una palabra: "¡Uf!"


-1

Alternativamente, puede hacer lo siguiente:

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

Y obtenga la descripción con lo siguiente:

string s = description[FunkyAttributesEnum.NameWithoutSpaces1];

En mi opinión, esta es una forma más eficiente de hacer lo que desea lograr, ya que no se necesita reflexión.


2
Claro, pero la reflexión no es tan mala como la gente cree que es.
Bryan Rowe el

No digo que sea malo, lo uso todo el tiempo. Sin embargo, a menudo se usa innecesariamente. :)
Ian P

44
Esta solución aleja la descripción del enum, creando al menos dos grandes problemas. Primero, si alguien agrega una nueva constante de enumeración, necesitará saber para ir a este otro lugar para agregar una entrada allí también. Los atributos son una señal clara para el responsable de lo que deben hacer. Mi segundo problema es que es mucho más código. Los atributos son compactos.
scobi

1
@scott pero le permite especificar su propio orden y excluir valores que no desea mostrar, que casi siempre es lo que realmente quiero
Simon_Weaver

-2

También puede definir un valor de enumeración como Name_Without_Spaces, y cuando desee una descripción, use Name_Without_Spaces.ToString().Replace('_', ' ')para reemplazar los guiones bajos por espacios.


8
Esta es una solución muy poco elegante. Considere usar la solución provista por @Bryan
Johann
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.