Como obter o atributo de nome para exibição de um membro do Enum por meio do código de máquina MVC?

211

Eu tenho uma propriedade no meu modelo chamada "Promoção" cujo tipo é uma enumeração de bandeira chamada "UserPromotion". Os membros da minha enum têm atributos de exibição definidos da seguinte maneira:

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

Agora, quero poder criar digamos um ul na minha opinião para mostrar os valores selecionados da minha propriedade "Promoção". Foi o que fiz até agora, mas o problema é que como posso obter os nomes para exibição aqui?

<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
fonte
12
O MVC5 suporta o atributo DisplayName em enumerações.
Bart Calixto
10
Para ser mais claro: Somente System.ComponentModel.DataAnnotations.DisplayAttribute. Não System.ComponentModel.DisplayNameAttribute.
Kamranicus
1
Isso inclui o uso de reflexão e, portanto, afeta o desempenho? porque isso vai ser chamado de muito tempo.
Nico

Respostas:

182

ATUALIZAR

A primeira solução foi focada em obter nomes de exibição do enum. O código abaixo deve ser a solução exata para o seu problema.

Você pode usar esta classe auxiliar para enumerações:

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

E então você pode usá-lo em sua exibição da seguinte maneira:

<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 que ajude! :)

Hrvoje Stanisic
fonte
8
Todas as respostas são usadas .ToString, mas em stackoverflow.com/q/483794/179311 , ele diz para usar Enum.GetName.
bradlis7
value.GetType (). GetField (value.ToString ()) era exatamente o que eu estava procurando!
CDIE
Essa resposta é adequada com algumas verificações nulas, mas se você não estiver usando a dotfuscation, a resposta em stackoverflow.com/a/4412730/852806 parecerá mais simples.
HockeyJ
5
Em GetDisplayValueque você deve primeiro teste descriptionAttributes == nullantes de tentar acessar a matriz: descriptionAttributes[0]. Caso contrário, você poderá gerar uma exceção e a linha abaixo, onde você verifica nulo, nunca será verdadeira.
Robert S.
Eu sugeriria alterações menores: IList estático público <T> GetValues ​​(valor Enum) poderia ser público IList <T> GetValues ​​(valor Enum). EnumHelper <T> para => classe estática pública EnumHelper <T> em que T: struct, IConvertible. Talvez contratador estático? static EnumHelper () {if (! typeof (T) .IsEnum) {lança nova ArgumentException ("T deve ser um tipo enumerado"); }}
Tom
172

Um revestimento - Sintaxe fluente

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

Exemplo

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

Resultado

Que estação é essa?
É verão

Aydin
fonte
2
Não existe uma definição de GetCustomAttribute
Tito
3
@Tito garantir que o projeto tem como alvo .NET Framework 4.5e que você está incluindo os seguintes namespacesSystem.Net System.ComponentModel.DataAnnotations
Aydin
8
using System.Reflection; using System.ComponentModel.DataAnnotations; Era necessário para mim.
Sinned Lolwut
1
que convenção de nomes terrível!
curiousBoy
@curiousBoy Como é GetAttribute<TAttribute>uma convenção de nomes terrível? Ele recupera o atributo que você especifica e usa maiúsculas e minúsculas pascal como todos os métodos públicos devem.
Aydin
137

Com base na grande resposta de Aydin , aqui está um método de extensão que não requer nenhum parâmetro de tipo.

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 () deve ser usado em vez da propriedade Name. Isso garante que a cadeia localizada será retornada se estiver usando a propriedade de atributo ResourceType.

Exemplo

Para usá-lo, basta referenciar o valor enum na sua exibição.

@{
    UserPromotion promo = UserPromotion.SendJobOffersByMail;
}

Promotion: @promo.GetDisplayName()

Resultado

Promoção: Enviar ofertas de emprego por e-mail

Todd
fonte
4
Certifique-se de adicionar os seguintes namespaces: using System; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Reflection;
Peter Kerr
Solução Slick, mas eu me { "Os modelos podem ser usados somente com o acesso de campo, acesso a propriedade, o índice de vetor de dimensão única, ou de parâmetro único expressões indexador personalizado."}
Casey Crookston
Olhando para outras respostas do SO para esta mensagem de erro (não estou familiarizado com ela), parece que você pode estar usando isso de dentro de um método auxiliar Html (como @Html.DisplayFor(m => m.myEnum.GetDisplayName()), por exemplo, o que não funcionará, porque eles esperam que a expressão avaliada produza uma propriedade ou algo parecido.Você deve usar o valor de enum bare como no exemplo acima.
Todd
7
Eu adicionei uma verificação de referência nula ao resultado GetCustomAttribute<DisplayAttribute>()porque, para alguns Enums, talvez isso não esteja presente. Faz parte de enumValue.ToString()se o DisplayAttribute não estava presente.
H Dog
1
Eu usei isso para criar um List<SelectListItem>que foi preenchido por um Enum com todas as DisplayAttribute.Nameanotações individuais - isso funcionou perfeitamente, obrigado !! 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() }); }
213 Hopper
61

Com base na resposta de Aydin, eu sugeriria uma implementação menos "duvidosa" (porque poderíamos facilmente obter Typeo Enumvalor do próprio valor, em vez de fornecê-lo como um parâmetro 😉:

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

EDIT (baseado no comentário de @Vahagn Nahapetyan)

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

Agora podemos usá-lo muito limpo desta maneira:

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

O que resulta em

"O sonho"

Bernoulli IT
fonte
1
De longe, a mais simples e fácil de todas as respostas. Obrigado!
precisa saber é o seguinte
Você deve ter cuidado com .First (). Isto irá lançar uma exceção, por exemplo, se o seu nome enum é "igual"
Vahagn Nahapetyan
Eu entendo o "perigo" com First (). Nesse caso em particular, isso não parece ser um problema. Porque é um método de extensão em que thisdeve ser um valor Enum válido (não nulo). Caso contrário, a chamada do método já seria lançada (que é de responsabilidade do código de chamada). Isso faz com que GetType(), com certeza, forneça o tipo de enum correto no qual, enumvaluecom certeza, será um membro. Mas GetCustomAttribute pode retornar um valor nulo, por isso forneci uma versão não excepcional do método para retornar nulo quando a cadeia de chamadas de método tiver um valor de retorno nulo em algum lugar. Obrigado!
Bernoulli IT
1
Para a segunda variante do seu código, parece que não há necessidade de usar o operador condicional nulo após GetMember porque esse método sempre retorna uma matriz de MemberInfo e nunca retorna nulo. E para mim parece que é melhor usar FirstOrDefault em vez de apenas First. Em seguida, o uso do operador condicional nulo após FirstOrDefault será considerado consistente.
Alex34758
28

Se você estiver usando o MVC 5.1 ou superior, existe uma maneira mais simples e clara: basta usar a anotação de dados (do System.ComponentModel.DataAnnotationsespaço para nome) como abaixo:

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

E, em vista, basta colocá-lo no auxiliar html adequado:

@Html.EnumDropDownListFor(model => model.Color)
1_bug
fonte
@SegmentationFault por quê? Você pode descrever seu problema? Qual versão do .NET / MVC você usa? Que erro você recebeu? Por favor seja mais específico.
1_bug
6
Porque funciona apenas para Dropdowns, não em nenhum outro lugar.
Falha na segmentação
2
Não parece existir no núcleo .net
Lonefish
3
.net usos principais Html.GetEnumSelectList (typeof (YourEnum)) @Lonefish
Patrick Mcvay
2
se quisermos usar o @ Html.DisplayFor (yourEnumField), podemos colocar um Enum.cshtml no diretório DisplayTemplates (no diretório compartilhado). neste arquivo, precisamos colocar apenas 2 linhas. o primeiro é: "@model Enum" o segundo é: "@GetDisplayName (Model)." o método GetDisplayName precisa ser como no @Bernoulli IT answare
Desenvolvedor
11

Você pode usar o método Type.GetMember e obter as informações do atributo usando a reflexão:

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

Havia alguns posts semelhantes aqui:

Obtendo atributos do valor de Enum

Como fazer o MVC3 DisplayFor mostrar o valor do atributo de exibição de um Enum?

maximpa
fonte
8

Com base na grande resposta de Todd, que se baseia na grande resposta de Aydin , aqui está um método de extensão genérico que não requer nenhum parâmetro de tipo.

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

Eu precisava disso para o meu projeto, porque algo como o código abaixo, onde nem todos os membros da enum têm um DisplayAttribute, não funciona com a solução 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

Se esta for uma solução complicada para um problema simples, entre em contato, mas essa foi a correção que usei.

Sinjai
fonte
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
fonte
Não funciona: / Estou recebendo um erroInvalidOperationException: Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions.
Muflix 02/04
6

Eu tenho duas soluções para esta pergunta.

  1. A primeira solução é obter nomes de exibição do 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. A segunda solução é obter o nome de exibição do nome do enum, mas ele será dividido no idioma do desenvolvedor chamado patch.
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
fonte
5

Para o ASP.Net Core 3.0, isso funcionou para mim (crédito aos respondentes anteriores).

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

Classe de modelo My View:

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

Um exemplo de uma exibição de navalha exibindo um rótulo e uma lista suspensa. Observe que a lista suspensa não requer um 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
fonte
Eu adicionaria uma verificação no método GetDisplayName de retorno string.IsNullOrEmpty (retVal)? enumValue.ToString (): retVal;
Sniipe 26/02
4

Você precisa usar um pouco de reflexão para acessar esse 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;

Eu recomendo agrupar esse método em um método de extensão ou executá-lo em um modelo de exibição.

alexn
fonte
4

Com o Core 2.1,

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

combinando todos os casos de borda acima:

  • membros da enumeração com nomes dos membros do objeto base ( Equals,ToString )
  • Displayatributo opcional

aqui está o meu 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
fonte
Solução agradável que lida com o atributo opcional Display. Obrigado!
Wellspring
3

Sinto muito por fazer isso, mas não pude usar nenhuma das outras respostas como está e não tenho tempo para discutir isso nos comentários.

Usa sintaxe 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
fonte
2

Desenvolvendo ainda mais as respostas de Aydin e Todd, aqui está um método de extensão que também permite que você obtenha o nome de um arquivo de recurso

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

e use-o como

public enum Season 
{
    [Display(ResourceType = typeof(Resource), Name = Season_Summer")]
    Summer
}
Peter Kerr
fonte
Estou tentando fazer isso funcionar no meu projeto, mas recebo um erro com o "novo ResourceManager (recurso) .GetString (nome);" linha. Eu fiz uma pergunta ( stackoverflow.com/questions/31319251/… ) e fui enviado para cá. Quando visualizo o "ResourceManager (recurso)" durante a execução, ele retorna "Resources.Enums.resource". Qualquer ajuda seria muito apreciada. Obrigado!
Karinne
Atualizado o código para melhores nulos punho quando você não tiver definido Display Name para alguns dos valores enum - poderia ajudar
Peter Kerr
Isso ainda não funcionou. Atualizei minha pergunta em stackoverflow.com/questions/31319251/… com a mensagem de erro. Obrigado pela ajuda!
Karinne
1

Quero contribuir com a extensão de enumeração GetDisplayName dependente da cultura. Espero que isso seja útil para qualquer pessoa pesquisando esta resposta como eu anteriormente:

"standart", como Aydin Adn e Todd mencionaram:

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

Maneira "dependente da 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
fonte
1

Atualização 2020: Uma versão atualizada da função fornecida por muitos neste segmento, mas agora para o C # 7.3 em diante:

Agora você pode restringir métodos genéricos a tipos de enumerações para poder escrever uma única extensão de método para usá-la com todas as enumerações desta forma:

O método de extensão 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();
} 

O enum:

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

Como usá-lo:

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

Bônus, enumerações com sinalizadores: se você estiver lidando com enumerações normais, a função acima é suficiente, mas se alguma de suas enumerações puder assumir vários valores com o uso de sinalizadores, será necessário modificá-lo desta forma (este código usa C # 8 recursos):

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

    } 

A enumeração com bandeiras:

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

Como usá-lo:

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

Com base nas respostas anteriores, criei este ajudante confortável para oferecer suporte a todas as propriedades DisplayAttribute de maneira legível:

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
fonte
0

Eu tentei fazer isso como uma edição, mas ela foi rejeitada; Não vejo o porquê.

O exemplo acima lançará uma exceção se você chamá-lo com um Enum que possui uma mistura de atributos personalizados e itens simples, por exemplo

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

Portanto, modifiquei o código levemente para verificar atributos personalizados antes de tentar acessá-los e usar o nome se nenhum for encontrado.

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

Usando o MVC5, você pode 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
}

então, se você deseja criar um seletor suspenso, pode usar:

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