¿Cómo obtener el atributo de nombre para mostrar de un miembro de Enum a través del código de MVC razor?

211

Tengo una propiedad en mi modelo llamada "Promoción" que su tipo es una enumeración de bandera llamada "UserPromotion". Los miembros de mi enumeración tienen atributos de visualización establecidos de la siguiente manera:

[Flags]
public enum UserPromotion
{
    None = 0x0,

    [Display(Name = "Send Job Offers By Mail")]
    SendJobOffersByMail = 0x1,

    [Display(Name = "Send Job Offers By Sms")]
    SendJobOffersBySms = 0x2,

    [Display(Name = "Send Other Stuff By Sms")]
    SendPromotionalBySms = 0x4,

    [Display(Name = "Send Other Stuff By Mail")]
    SendPromotionalByMail = 0x8
}

Ahora quiero poder crear say a ul en mi vista para mostrar los valores seleccionados de mi propiedad "Promoción". Esto es lo que he hecho hasta ahora, pero el problema es que ¿cómo puedo obtener los nombres para mostrar aquí?

<ul>
    @foreach (int aPromotion in @Enum.GetValues(typeof(UserPromotion)))
    {
        var currentPromotion = (int)Model.JobSeeker.Promotion;
        if ((currentPromotion & aPromotion) == aPromotion)
        {
        <li>Here I don't know how to get the display attribute of "currentPromotion".</li>
        }
    }
</ul>
Pejman
fuente
12
MVC5 admite el atributo DisplayName en las enumeraciones.
Bart Calixto
10
Para ser más claro: solo System.ComponentModel.DataAnnotations.DisplayAttribute. No System.ComponentModel.DisplayNameAttribute.
kamranicus
1
¿Esto incluye el uso de la reflexión y, por lo tanto, afecta el rendimiento? Porque esto se llamará MUCHO tiempo.
Nico

Respuestas:

182

ACTUALIZAR

La primera solución se centró en obtener nombres para mostrar de enum. El siguiente código debe ser la solución exacta para su problema.

Puede usar esta clase auxiliar para enumeraciones:

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;

public static class EnumHelper<T>
{
    public static IList<T> GetValues(Enum value)
    {
        var enumValues = new List<T>();

        foreach (FieldInfo fi in value.GetType().GetFields(BindingFlags.Static | BindingFlags.Public))
        {
            enumValues.Add((T)Enum.Parse(value.GetType(), fi.Name, false));
        }
        return enumValues;
    }

    public static T Parse(string value)
    {
        return (T)Enum.Parse(typeof(T), value, true);
    }

    public static IList<string> GetNames(Enum value)
    {
        return value.GetType().GetFields(BindingFlags.Static | BindingFlags.Public).Select(fi => fi.Name).ToList();
    }

    public static IList<string> GetDisplayValues(Enum value)
    {
        return GetNames(value).Select(obj => GetDisplayValue(Parse(obj))).ToList();
    }

    private static string lookupResource(Type resourceManagerProvider, string resourceKey)
    {
        foreach (PropertyInfo staticProperty in resourceManagerProvider.GetProperties(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public))
        {
            if (staticProperty.PropertyType == typeof(System.Resources.ResourceManager))
            {
                System.Resources.ResourceManager resourceManager = (System.Resources.ResourceManager)staticProperty.GetValue(null, null);
                return resourceManager.GetString(resourceKey);
            }
        }

        return resourceKey; // Fallback with the key name
    }

    public static string GetDisplayValue(T value)
    {
        var fieldInfo = value.GetType().GetField(value.ToString());

        var descriptionAttributes = fieldInfo.GetCustomAttributes(
            typeof(DisplayAttribute), false) as DisplayAttribute[];

        if (descriptionAttributes[0].ResourceType != null)
            return lookupResource(descriptionAttributes[0].ResourceType, descriptionAttributes[0].Name);

        if (descriptionAttributes == null) return string.Empty;
        return (descriptionAttributes.Length > 0) ? descriptionAttributes[0].Name : value.ToString();
    }
}

Y luego puede usarlo en su vista de la siguiente manera:

<ul>
    @foreach (var value in @EnumHelper<UserPromotion>.GetValues(UserPromotion.None))
    {
         if (value == Model.JobSeeker.Promotion)
        {
            var description = EnumHelper<UserPromotion>.GetDisplayValue(value);
            <li>@Html.DisplayFor(e => description )</li>
        }
    }
</ul>

¡Espero eso ayude! :)

Hrvoje Stanisic
fuente
8
Todas las respuestas usan .ToString, pero desde stackoverflow.com/q/483794/179311 , dice usar Enum.GetNameen su lugar.
bradlis7
value.GetType (). GetField (value.ToString ()) era exactamente lo que estaba buscando!
cdie
Esta respuesta está bien con algunas comprobaciones nulas agregadas, pero si no está utilizando dotfuscation, la respuesta en stackoverflow.com/a/4412730/852806 parece más simple.
HockeyJ
55
En GetDisplayValueprimer lugar debería prueba descriptionAttributes == nullantes de intentar acceder a la matriz: descriptionAttributes[0]. De lo contrario, puede plantear una excepción y la línea a continuación donde verifica si es nula nunca será cierta.
Robert S.
Sugeriría cambios menores: public static IList <T> GetValues ​​(valor Enum) podría ser public static IList <T> GetValues ​​(valor T). EnumHelper <T> to => clase estática pública EnumHelper <T> donde T: struct, IConvertible. Tal vez contructor estático? static EnumHelper () {if (! typeof (T) .IsEnum) {throw new ArgumentException ("T debe ser un tipo enumerado"); }}
Tom
172

One liner - Sintaxis fluida

public static class Extensions
{
    /// <summary>
    ///     A generic extension method that aids in reflecting 
    ///     and retrieving any attribute that is applied to an `Enum`.
    /// </summary>
    public static TAttribute GetAttribute<TAttribute>(this Enum enumValue) 
            where TAttribute : Attribute
    {
        return enumValue.GetType()
                        .GetMember(enumValue.ToString())
                        .First()
                        .GetCustomAttribute<TAttribute>();
    }
}

Ejemplo

public enum Season 
{
   [Display(Name = "It's autumn")]
   Autumn,

   [Display(Name = "It's winter")]
   Winter,

   [Display(Name = "It's spring")]
   Spring,

   [Display(Name = "It's summer")]
   Summer
}

public class Foo 
{
    public Season Season = Season.Summer;

    public void DisplayName()
    {
        var seasonDisplayName = Season.GetAttribute<DisplayAttribute>();
        Console.WriteLine("Which season is it?");
        Console.WriteLine (seasonDisplayName.Name);
    } 
}

Salida

Que temporada es
Es verano

Aydin
fuente
2
No existe una definición de GetCustomAttribute
Tito
3
@Tito asegúrese de que su proyecto esté dirigido .NET Framework 4.5y que incluya los siguientes espacios de nombresSystem.Net System.ComponentModel.DataAnnotations
Aydin
8
usando System.Reflection; usando System.ComponentModel.DataAnnotations; Fue necesario para mi.
Sinned Lolwut
1
¡Qué terrible convención de nombres!
curiosoBoy
@curiousBoy ¿Cómo es GetAttribute<TAttribute>una convención de nombres terrible? Recupera el atributo que especifique y utiliza la carcasa pascal como deberían hacerlo todos los métodos públicos.
Aydin
137

Sobre la base de la gran respuesta de Aydin , aquí hay un método de extensión que no requiere ningún tipo de parámetro.

using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;

public static class EnumExtensions
{
    public static string GetDisplayName(this Enum enumValue)
    {
        return enumValue.GetType()
                        .GetMember(enumValue.ToString())
                        .First()
                        .GetCustomAttribute<DisplayAttribute>()
                        .GetName();
    }
}

NOTA: GetName () debe usarse en lugar de la propiedad Name. Esto garantiza que la cadena localizada se devolverá si se usa la propiedad de atributo ResourceType.

Ejemplo

Para usarlo, solo haga referencia al valor de enumeración en su vista.

@{
    UserPromotion promo = UserPromotion.SendJobOffersByMail;
}

Promotion: @promo.GetDisplayName()

Salida

Promoción: envíe ofertas de trabajo por correo

Todd
fuente
44
Asegúrese de agregar los siguientes espacios de nombres: using System; usando System.ComponentModel.DataAnnotations; usando System.Linq; usando System.Reflection;
Peter Kerr
Solución ingeniosa, pero obtengo {"Las plantillas solo se pueden usar con acceso de campo, acceso a propiedades, índice de matriz de una sola dimensión o expresiones de indexador personalizado de un solo parámetro."}
Casey Crookston
Al mirar otras respuestas SO para este mensaje de error (no estoy familiarizado con él), parece que podría estar usando esto desde un método auxiliar Html (como @Html.DisplayFor(m => m.myEnum.GetDisplayName()), que no funcionará, porque esperan que la expresión evaluada produzca una propiedad o algo similar. Debe usar el valor de enumeración simple como en el ejemplo anterior.
Todd
77
GetCustomAttribute<DisplayAttribute>()Agregué una verificación de referencia nula al resultado de porque para algunas Enums tal vez esto no esté presente. Vuelve a enumValue.ToString()si DisplayAttribute no estaba presente.
H Dog
1
Utilicé esto para crear List<SelectListItem>un Enum poblado por un Enum con todas las DisplayAttribute.Nameanotaciones individuales . ¡Esto funcionó perfectamente, gracias! public List<SelectListItem> MySelectListItem = new List<SelectListItem>(); foreach (MyEnum MyEnum in Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>().Where(x => x != MyEnum.Default)) { MySelectListItem.Add(new SelectListItem() { Text = MyEnum.GetDisplayName(), Value = ((int)MyEnum).ToString() }); }
Hopper
61

Basado en la respuesta de Aydin, sugeriría una implementación menos "duplicativa" (porque podríamos obtener fácilmente Typeel Enumvalor en sí mismo, en lugar de proporcionarlo como un parámetro 😉:

public static string GetDisplayName(this Enum enumValue)
{
    return enumValue.GetType().GetMember(enumValue.ToString())
                   .First()
                   .GetCustomAttribute<DisplayAttribute>()
                   .Name;
}

EDITAR (basado en el comentario de @Vahagn Nahapetyan)

public static string GetDisplayName(this Enum enumValue)
{
    return enumValue.GetType()?
                    .GetMember(enumValue.ToString())?
                    .First()?
                    .GetCustomAttribute<DisplayAttribute>()?
                    .Name;
}

Ahora podemos usarlo muy limpio de esta manera:

public enum Season 
{
    [Display(Name = "The Autumn")]
    Autumn,

    [Display(Name = "The Weather")]
    Winter,

    [Display(Name = "The Tease")]
    Spring,

    [Display(Name = "The Dream")]
    Summer
}

Season.Summer.GetDisplayName();

Lo que resulta en

"El sueño"

Bernoulli IT
fuente
1
Con mucho, la más simple y fácil de todas las respuestas. ¡Gracias!
Casey Crookston el
Debe tener cuidado con .First (). Esto arrojará una excepción, por ejemplo, si su nombre de enumeración es "Igual"
Vahagn Nahapetyan
Entiendo el "peligro" con First (). En este caso particular, no parece un problema. Porque es un método de extensión donde thisdebe ser un valor Enum válido (no nulo). De lo contrario, llamar al método ya arrojaría (que es responsabilidad del código de llamada). Esto hace que GetType()seguramente proporcione el tipo de enumeración correcto en el que enumvalueseguramente será miembro. Pero GetCustomAttribute podría devolver un valor nulo, por lo que proporcioné una versión no excepcional del método para devolver nulo cuando la cadena de llamadas de método tiene un valor de retorno nulo en alguna parte. ¡Gracias!
Bernoulli IT
1
Para la segunda variante de su código, parece que no hay necesidad de usar un operador condicional nulo después de GetMember porque este método siempre devuelve una matriz de MemberInfo y nunca devuelve nulo. Y para mí parece que es mejor usar FirstOrDefault en lugar de solo First. Entonces, el uso del operador condicional nulo después de FirstOrDefault se verá consistente.
Alex34758
28

Si está utilizando MVC 5.1 o superior, hay una manera más simple y clara: simplemente use la anotación de datos (del System.ComponentModel.DataAnnotationsespacio de nombres) como se muestra a continuación:

public enum Color
{
    [Display(Name = "Dark red")]
    DarkRed,
    [Display(Name = "Very dark red")]
    VeryDarkRed,
    [Display(Name = "Red or just black?")]
    ReallyDarkRed
}

Y a la vista, simplemente colóquelo en el html helper apropiado:

@Html.EnumDropDownListFor(model => model.Color)
1_bug
fuente
@SegmentationFault ¿por qué? ¿Puedes describir tu problema? ¿Qué versión de .NET / MVC usas? ¿Qué error tienes? Por favor sé más específico.
1_bug
66
Porque solo funciona para menús desplegables, no en ningún otro lugar.
Falla de segmentación el
2
No parece existir en .net core
Lonefish
3
.net core usa Html.GetEnumSelectList (typeof (YourEnum)) @Lonefish
Patrick Mcvay el
2
si queremos usar @ Html.DisplayFor (yourEnumField) podemos poner un Enum.cshtml en el directorio DisplayTemplates (en el directorio compartido). en este archivo necesitamos poner solo 2 líneas. el primero es: "@model Enum" el segundo es: "@GetDisplayName (Model)". el método GetDisplayName debe ser como en @Bernoulli IT answare
Desarrollador
11

Puede usar el Método Type.GetMember , luego obtener la información del atributo usando la reflexión:

// display attribute of "currentPromotion"

var type = typeof(UserPromotion);
var memberInfo = type.GetMember(currentPromotion.ToString());
var attributes = memberInfo[0].GetCustomAttributes(typeof(DisplayAttribute), false);
var description = ((DisplayAttribute)attributes[0]).Name;

Hubo algunas publicaciones similares aquí:

Obteniendo atributos del valor de Enum

¿Cómo hacer que MVC3 DisplayFor muestre el valor de un atributo de visualización de Enum?

maximpa
fuente
8

Sobre la base de la gran respuesta de Todd, que se basa en la gran respuesta de Aydin , aquí hay un método de extensión genérico que no requiere ningún tipo de parámetro.

/// <summary>
/// Gets human-readable version of enum.
/// </summary>
/// <returns>DisplayAttribute.Name property of given enum.</returns>
public static string GetDisplayName<T>(this T enumValue) where T : IComparable, IFormattable, IConvertible
{
    if (!typeof(T).IsEnum)
        throw new ArgumentException("Argument must be of type Enum");

    DisplayAttribute displayAttribute = enumValue.GetType()
                                                 .GetMember(enumValue.ToString())
                                                 .First()
                                                 .GetCustomAttribute<DisplayAttribute>();

    string displayName = displayAttribute?.GetName();

    return displayName ?? enumValue.ToString();
}

Necesitaba esto para mi proyecto porque algo como el siguiente código, donde no todos los miembros de la enumeración tienen DisplayAttribute, no funciona con la solución de Todd:

public class MyClass
{
    public enum MyEnum 
    {
        [Display(Name="ONE")]
        One,
        // No DisplayAttribute
        Two
    }
    public void UseMyEnum()
    {
        MyEnum foo = MyEnum.One;
        MyEnum bar = MyEnum.Two;
        Console.WriteLine(foo.GetDisplayName());
        Console.WriteLine(bar.GetDisplayName());
    }
}
// Output:
//
// ONE
// Two

Si esta es una solución complicada a un problema simple, avíseme, pero esta fue la solución que usé.

Sinjai
fuente
6
<ul>
    @foreach (int aPromotion in @Enum.GetValues(typeof(UserPromotion)))
    {
        var currentPromotion = (int)Model.JobSeeker.Promotion;
        if ((currentPromotion & aPromotion) == aPromotion)
        {
        <li>@Html.DisplayFor(e => currentPromotion)</li>
        }
    }
</ul>
Dmytro
fuente
No funciona: / Recibo un errorInvalidOperationException: Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions.
Muflix
6

Tengo dos soluciones para esta pregunta.

  1. La primera solución es obtener nombres para mostrar de enum.
public enum CourseLocationTypes
{
    [Display(Name = "On Campus")]
    OnCampus,
    [Display(Name = "Online")]
    Online,
    [Display(Name = "Both")]
    Both
}

public static string DisplayName(this Enum value)
{
    Type enumType = value.GetType();
    string enumValue = Enum.GetName(enumType, value);
    MemberInfo member = enumType.GetMember(enumValue)[0];

    object[] attrs = member.GetCustomAttributes(typeof(DisplayAttribute), false);
    string outString = ((DisplayAttribute)attrs[0]).Name;

    if (((DisplayAttribute)attrs[0]).ResourceType != null)
    {
        outString = ((DisplayAttribute)attrs[0]).GetName();
    }

    return outString;
}
<h3 class="product-title white">@Model.CourseLocationType.DisplayName()</h3>
  1. La segunda solución es obtener el nombre para mostrar del nombre enum, pero se dividirá enum en el lenguaje de desarrollador que se llama parche.
public static string SplitOnCapitals(this string text)
{
        var r = new Regex(@"
            (?<=[A-Z])(?=[A-Z][a-z]) |
             (?<=[^A-Z])(?=[A-Z]) |
             (?<=[A-Za-z])(?=[^A-Za-z])", RegexOptions.IgnorePatternWhitespace);

        return r.Replace(text, " ");
}
 <div class="widget-box pt-0">
     @foreach (var item in Enum.GetNames(typeof(CourseLocationType)))
     {
         <label class="pr-2 pt-1">
             @Html.RadioButtonFor(x => x.CourseLocationType, item, new { type = "radio", @class = "iCheckBox control-label" })&nbsp; @item.SplitOnCapitals()
         </label>
     }
     @Html.ValidationMessageFor(x => x.CourseLocationType)
 </div>
Yasin Sunni
fuente
5

Para ASP.Net Core 3.0, esto funcionó para mí (crédito a respuestas anteriores).

Mi clase de Enum:

using System;
using System.Linq;
using System.ComponentModel.DataAnnotations;
using System.Reflection;

public class Enums
{
    public enum Duration
    { 
        [Display(Name = "1 Hour")]
        OneHour,
        [Display(Name = "1 Day")]
        OneDay
    }

    // Helper method to display the name of the enum values.
    public static string GetDisplayName(Enum value)
    {
        return value.GetType()?
       .GetMember(value.ToString())?.First()?
       .GetCustomAttribute<DisplayAttribute>()?
       .Name;
    }
}

Mi clase de modelo de vista:

public class MyViewModel
{
    public Duration Duration { get; set; }
}

Un ejemplo de una vista de afeitar que muestra una etiqueta y una lista desplegable. Observe que la lista desplegable no requiere un método auxiliar:

@model IEnumerable<MyViewModel> 

@foreach (var item in Model)
{
    <label asp-for="@item.Duration">@Enums.GetDisplayName(item.Duration)</label>
    <div class="form-group">
        <label asp-for="@item.Duration" class="control-label">Select Duration</label>
        <select asp-for="@item.Duration" class="form-control"
            asp-items="Html.GetEnumSelectList<Enums.Duration>()">
        </select>
    </div>
}
Mar, oceano
fuente
Agregaría una verificación en la cadena de retorno del método GetDisplayName. IsNullOrEmpty (retVal)? enumValue.ToString (): retVal;
Sniipe
4

Debe usar un poco de reflexión para acceder a ese atributo:

var type = typeof(UserPromotion);
var member = type.GetMember(Model.JobSeeker.Promotion.ToString());
var attributes = member[0].GetCustomAttributes(typeof(DisplayAttribute), false);
var name = ((DisplayAttribute)attributes[0]).Name;

Recomiendo envolver este método en un método de extensión o realizar esto en un modelo de vista.

alexn
fuente
4

Con Core 2.1,

public static string GetDisplayName(Enum enumValue)
{
  return enumValue.GetType()?
 .GetMember(enumValue.ToString())?[0]?
 .GetCustomAttribute<DisplayAttribute>()?
 .Name;
}
Deniz aydın
fuente
4

combinando todos los casos de borde juntos desde arriba:

  • miembros de enumeración con nombres de miembros de objeto base ( Equals, ToString)
  • Displayatributo opcional

Aquí está mi código:

public enum Enum
{
    [Display(Name = "What a weird name!")]
    ToString,

    Equals
}

public static class EnumHelpers
{
    public static string GetDisplayName(this Enum enumValue)
    {
        var enumType = enumValue.GetType();

        return enumType
                .GetMember(enumValue.ToString())
                .Where(x => x.MemberType == MemberTypes.Field && ((FieldInfo)x).FieldType == enumType)
                .First()
                .GetCustomAttribute<DisplayAttribute>()?.Name ?? enumValue.ToString();
    }
}

void Main()
{
    Assert.Equals("What a weird name!", Enum.ToString.GetDisplayName());
    Assert.Equals("Equals", Enum.Equals.GetDisplayName());
}
avs099
fuente
Buena solución que maneja el atributo opcional Display. ¡Gracias!
Wellspring
3

Lamento hacer esto, pero no pude usar ninguna de las otras respuestas tal cual y no tengo tiempo para dudarlo en los comentarios.

Utiliza la sintaxis C # 6.

static class EnumExtensions
{
    /// returns the localized Name, if a [Display(Name="Localised Name")] attribute is applied to the enum member
    /// returns null if there isnt an attribute
    public static string DisplayNameOrEnumName(this Enum value)
    // => value.DisplayNameOrDefault() ?? value.ToString()
    {
        // More efficient form of ^ based on http://stackoverflow.com/a/17034624/11635
        var enumType = value.GetType();
        var enumMemberName = Enum.GetName(enumType, value);
        return enumType
            .GetEnumMemberAttribute<DisplayAttribute>(enumMemberName)
            ?.GetName() // Potentially localized
            ?? enumMemberName; // Or fall back to the enum name
    }

    /// returns the localized Name, if a [Display] attribute is applied to the enum member
    /// returns null if there is no attribute
    public static string DisplayNameOrDefault(this Enum value) =>
        value.GetEnumMemberAttribute<DisplayAttribute>()?.GetName();

    static TAttribute GetEnumMemberAttribute<TAttribute>(this Enum value) where TAttribute : Attribute =>
        value.GetType().GetEnumMemberAttribute<TAttribute>(value.ToString());

    static TAttribute GetEnumMemberAttribute<TAttribute>(this Type enumType, string enumMemberName) where TAttribute : Attribute =>
        enumType.GetMember(enumMemberName).Single().GetCustomAttribute<TAttribute>();
}
Ruben Bartelink
fuente
2

Basándose en las respuestas de Aydin y Todd, aquí hay un método de extensión que también le permite obtener el nombre de un archivo de recursos

using AppResources;
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using System.Resources;

public static class EnumExtensions
{
    public static string GetDisplayName(this Enum enumValue)
    {
        var enumMember= enumValue.GetType()
                        .GetMember(enumValue.ToString());

        DisplayAttribute displayAttrib = null;
        if (enumMember.Any()) {
            displayAttrib = enumMember 
                        .First()
                        .GetCustomAttribute<DisplayAttribute>();
        }

        string name = null;
        Type resource = null;

        if (displayAttrib != null)
        {
            name = displayAttrib.Name;
            resource = displayAttrib.ResourceType;
        }

        return String.IsNullOrEmpty(name) ? enumValue.ToString()
            : resource == null ?  name
            : new ResourceManager(resource).GetString(name);
    }
}

y úsalo como

public enum Season 
{
    [Display(ResourceType = typeof(Resource), Name = Season_Summer")]
    Summer
}
Peter Kerr
fuente
Estoy tratando de hacer que esto funcione para mi proyecto, pero aparece un error con el "nuevo ResourceManager (recurso) .GetString (nombre);" línea. Hice una pregunta ( stackoverflow.com/questions/31319251/… ) y me enviaron aquí. Cuando veo el "ResourceManager (recurso)" mientras se ejecuta, devuelve "Resources.Enums.resource". Cualquier ayuda sería muy apreciada. ¡Gracias!
Karinne
Se actualizó el código para manejar mejor los valores nulos cuando no se ha configurado el Nombre para mostrar para algunos de los valores de enumeración - podría ayudar
Peter Kerr
Eso todavía no funcionó. Actualicé mi pregunta en stackoverflow.com/questions/31319251/… con el mensaje de error. ¡Gracias por la ayuda!
Karinne
1

Quiero contribuir con la extensión de enumeración GetDisplayName dependiente de la cultura. Espero que esto sea útil para cualquiera que busque en Google esta respuesta como yo anteriormente:

"estándar" como Aydin Adn y Todd mencionaron:

    public static string GetDisplayName(this Enum enumValue)
    {
        return enumValue
            .GetType()
            .GetMember(enumValue.ToString())
            .First()
            .GetCustomAttribute<DisplayAttribute>()
            .GetName();
    }

Forma "dependiente de la cultura":

    public static string GetDisplayName(this Enum enumValue, CultureInfo ci)
    {
        var displayAttr = enumValue
            .GetType()
            .GetMember(enumValue.ToString())
            .First()
            .GetCustomAttribute<DisplayAttribute>();

        var resMan = displayAttr.ResourceType?.GetProperty(@"ResourceManager", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).GetValue(null, null) as ResourceManager;

        return resMan?.GetString(displayAttr.Name, ci) ?? displayAttr.GetName();
    }
Pavel
fuente
1

Actualización 2020: una versión actualizada de la función proporcionada por muchos en este hilo pero ahora para C # 7.3 en adelante:

Ahora puede restringir los métodos genéricos a tipos de enumeraciones para que pueda escribir una sola extensión de método para usarla con todas sus enumeraciones de esta manera:

El método de extensión genérico:

public static string ATexto<T>(this T enumeración) where T : struct, Enum {
    var tipo = enumeración.GetType();
    return tipo.GetMember(enumeración.ToString())
    .Where(x => x.MemberType == MemberTypes.Field && ((FieldInfo)x).FieldType == tipo).First()
    .GetCustomAttribute<DisplayAttribute>()?.Name ?? enumeración.ToString();
} 

La enumeración:

public enum TipoImpuesto { 
IVA, INC, [Display(Name = "IVA e INC")]IVAeINC, [Display(Name = "No aplica")]NoAplica };

Cómo usarlo:

var tipoImpuesto = TipoImpuesto.IVAeINC;
var textoTipoImpuesto = tipoImpuesto.ATexto(); // Prints "IVA e INC".

Bono, enumeraciones con banderas: si se trata de enumeraciones normales, la función anterior es suficiente, pero si alguna de sus enumeraciones puede tomar múltiples valores con el uso de indicadores, deberá modificarla de esta manera (este código usa C # 8 caracteristicas):

    public static string ATexto<T>(this T enumeración) where T : struct, Enum {

        var tipo = enumeración.GetType();
        var textoDirecto = enumeración.ToString();

        string obtenerTexto(string textoDirecto) => tipo.GetMember(textoDirecto)
            .Where(x => x.MemberType == MemberTypes.Field && ((FieldInfo)x).FieldType == tipo)
            .First().GetCustomAttribute<DisplayAttribute>()?.Name ?? textoDirecto;

        if (textoDirecto.Contains(", ")) {

            var texto = new StringBuilder();
            foreach (var textoDirectoAux in textoDirecto.Split(", ")) {
                texto.Append($"{obtenerTexto(textoDirectoAux)}, ");
            }
            return texto.ToString()[0..^2];

        } else {
            return obtenerTexto(textoDirecto);
        }

    } 

La enumeración con banderas:

[Flags] public enum TipoContribuyente {
    [Display(Name = "Común")] Común = 1, 
    [Display(Name = "Gran Contribuyente")] GranContribuyente = 2, 
    Autorretenedor = 4, 
    [Display(Name = "Retenedor de IVA")] RetenedorIVA = 8, 
    [Display(Name = "Régimen Simple")] RégimenSimple = 16 } 

Cómo usarlo:

var tipoContribuyente = TipoContribuyente.RetenedorIVA | TipoContribuyente.GranContribuyente;
var textoAux = tipoContribuyente.ATexto(); // Prints "Gran Contribuyente, Retenedor de IVA".
David
fuente
0

Según las respuestas anteriores, he creado este cómodo ayudante para admitir todas las propiedades DisplayAttribute de una manera legible:

public static class EnumExtensions
    {
        public static DisplayAttributeValues GetDisplayAttributeValues(this Enum enumValue)
        {
            var displayAttribute = enumValue.GetType().GetMember(enumValue.ToString()).First().GetCustomAttribute<DisplayAttribute>();

            return new DisplayAttributeValues(enumValue, displayAttribute);
        }

        public sealed class DisplayAttributeValues
        {
            private readonly Enum enumValue;
            private readonly DisplayAttribute displayAttribute;

            public DisplayAttributeValues(Enum enumValue, DisplayAttribute displayAttribute)
            {
                this.enumValue = enumValue;
                this.displayAttribute = displayAttribute;
            }

            public bool? AutoGenerateField => this.displayAttribute?.GetAutoGenerateField();
            public bool? AutoGenerateFilter => this.displayAttribute?.GetAutoGenerateFilter();
            public int? Order => this.displayAttribute?.GetOrder();
            public string Description => this.displayAttribute != null ? this.displayAttribute.GetDescription() : string.Empty;
            public string GroupName => this.displayAttribute != null ? this.displayAttribute.GetGroupName() : string.Empty;
            public string Name => this.displayAttribute != null ? this.displayAttribute.GetName() : this.enumValue.ToString();
            public string Prompt => this.displayAttribute != null ? this.displayAttribute.GetPrompt() : string.Empty;
            public string ShortName => this.displayAttribute != null ? this.displayAttribute.GetShortName() : this.enumValue.ToString();
        }
    }
Kryszal
fuente
0

Intenté hacer esto como una edición pero fue rechazado; No puedo ver por qué.

Lo anterior arrojará una excepción si lo llama con una enumeración que tiene una combinación de atributos personalizados y elementos simples, por ejemplo

public enum CommentType
{
    All = 1,
    Rent = 2,
    Insurance = 3,
    [Display(Name="Service Charge")]
    ServiceCharge = 4
}

Así que modifiqué el código muy ligeramente para verificar los atributos personalizados antes de intentar acceder a ellos y usar el nombre si no se encuentra ninguno.

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;

public static class EnumHelper<T>
{
    public static IList<T> GetValues(Enum value)
    {
        var enumValues = new List<T>();

        foreach (FieldInfo fi in value.GetType().GetFields(BindingFlags.Static | BindingFlags.Public))
        {
            enumValues.Add((T)Enum.Parse(value.GetType(), fi.Name, false));
        }
        return enumValues;
    }

    public static T Parse(string value)
    {
        return (T)Enum.Parse(typeof(T), value, true);
    }

    public static IList<string> GetNames(Enum value)
    {
        return value.GetType().GetFields(BindingFlags.Static | BindingFlags.Public).Select(fi => fi.Name).ToList();
    }

    public static IList<string> GetDisplayValues(Enum value)
    {
        return GetNames(value).Select(obj => GetDisplayValue(Parse(obj))).ToList();
    }

    private static string lookupResource(Type resourceManagerProvider, string resourceKey)
    {
        foreach (PropertyInfo staticProperty in resourceManagerProvider.GetProperties(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public))
        {
            if (staticProperty.PropertyType == typeof(System.Resources.ResourceManager))
            {
                System.Resources.ResourceManager resourceManager = (System.Resources.ResourceManager)staticProperty.GetValue(null, null);
                return resourceManager.GetString(resourceKey);
            }
        }

        return resourceKey; // Fallback with the key name
    }

    public static string GetDisplayValue(T value)
    {
        var fieldInfo = value.GetType().GetField(value.ToString());

        var descriptionAttributes = fieldInfo.GetCustomAttributes(
            typeof(DisplayAttribute), false) as DisplayAttribute[];

        if (descriptionAttributes.Any() && descriptionAttributes[0].ResourceType != null)
            return lookupResource(descriptionAttributes[0].ResourceType, descriptionAttributes[0].Name);

        if (descriptionAttributes == null) return string.Empty;
        return (descriptionAttributes.Length > 0) ? descriptionAttributes[0].Name : value.ToString();
    }
}
rojo
fuente
0

Usando MVC5 podría usar:

public enum UserPromotion
{
   None = 0x0,

   [Display(Name = "Send Job Offers By Mail")]
   SendJobOffersByMail = 0x1,

   [Display(Name = "Send Job Offers By Sms")]
   SendJobOffersBySms = 0x2,

   [Display(Name = "Send Other Stuff By Sms")]
   SendPromotionalBySms = 0x4,

   [Display(Name = "Send Other Stuff By Mail")]
   SendPromotionalByMail = 0x8
}

entonces, si desea crear un selector desplegable, puede usar:

@Html.EnumDropdownListFor(expression: model => model.PromotionSelector, optionLabel: "Select") 
M.Hazara
fuente