Jak uzyskać atrybut wyświetlanej nazwy członka Enum za pomocą kodu maszynki MVC?

211

Mam w moim modelu właściwość o nazwie „Promocja”, że jej typ jest wyliczeniem flagi o nazwie „UserPromotion”. Członkowie mojego wyliczenia mają ustawione atrybuty wyświetlania w następujący sposób:

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

Teraz chcę być w stanie utworzyć powiedzmy ul w moim widoku, aby pokazać wybrane wartości mojej właściwości „Promocja”. Tak zrobiłem do tej pory, ale problem polega na tym, jak uzyskać tutaj nazwy wyświetlane?

<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
źródło
12
MVC5 obsługuje atrybut DisplayName w wyliczeniach.
Bart Calixto,
10
Aby być bardziej zrozumiałym: tylko System.ComponentModel.DataAnnotations.DisplayAttribute. Nie System.ComponentModel.DisplayNameAttribute.
kamranicus
1
Czy obejmuje to użycie refleksji, a zatem wpływa na wydajność? bo to się nazywa DUŻO czasu.
Nico,

Odpowiedzi:

182

AKTUALIZACJA

Pierwsze rozwiązanie koncentrowało się na uzyskiwaniu wyświetlanych nazw z wyliczenia. Poniższy kod powinien być dokładnym rozwiązaniem Twojego problemu.

Możesz użyć tej klasy pomocniczej do wyliczeń:

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

Następnie możesz użyć go w swoim widoku jako:

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

Mam nadzieję, że to pomoże! :)

Hrvoje Stanisic
źródło
8
Wszystkie odpowiedzi są używane .ToString, ale z stackoverflow.com/q/483794/179311 mówi się, aby użyć Enum.GetName.
bradlis7
value.GetType (). GetField (value.ToString ()) było dokładnie tym, czego szukałem!
cdie
Ta odpowiedź jest w porządku z dodanym sprawdzaniem wartości null, ale jeśli nie używasz dotfuscation, odpowiedź na stackoverflow.com/a/4412730/852806 wydaje się prostsza.
HokejJ
5
W GetDisplayValuewas powinien pierwszy test descriptionAttributes == nullprzed próby uzyskania dostępu do tablicy: descriptionAttributes[0]. W przeciwnym razie możesz zgłosić wyjątek, a wiersz poniżej, w którym sprawdzasz, czy null nigdy nie będzie prawdziwy.
Robert S.
Sugerowałbym niewielkie zmiany: publiczna statyczna IList <T> GetValues ​​(wartość Enum) może być publiczną statyczną IList <T> GetValues ​​(wartość T). EnumHelper <T> to => publiczna klasa statyczna EnumHelper <T> gdzie T: struct, IConvertible. Może statyczny konstruktor? static EnumHelper () {if (! typeof (T) .IsEnum) {wyrzuć nowy ArgumentException („T musi być typem wyliczonym”); }}
Tom
172

Jedna wkładka - płynna składnia

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

Przykład

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

Wynik

Która to pora roku?
Jest lato

Aydin
źródło
2
Nie istnieje definicja GetCustomAttribute
Tito
3
@Tito upewnij się, że Twój projekt jest kierowany .NET Framework 4.5i że dołączasz następujące przestrzenie nazwSystem.Net System.ComponentModel.DataAnnotations
Aydin
8
using System.Reflection; using System.ComponentModel.DataAnnotations; Był mi potrzebny.
Sinned Lolwut,
1
co za okropna konwencja nazywania!
ciekawyBoy
@curiousBoy Jak wygląda GetAttribute<TAttribute>straszna konwencja nazewnictwa? Pobiera określony atrybut i używa osłonki pascal, tak jak powinny to robić wszystkie metody publiczne.
Aydin
137

Opierając się na na doskonałej odpowiedzi Aydina , oto metoda rozszerzenia, która nie wymaga żadnych parametrów typu.

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

UWAGA: Zamiast właściwości Name należy użyć GetName (). Zapewnia to, że zlokalizowany ciąg zostanie zwrócony, jeśli zostanie użyta właściwość atrybutu ResourceType.

Przykład

Aby go użyć, po prostu odwołaj się do wartości wyliczenia w swoim widoku.

@{
    UserPromotion promo = UserPromotion.SendJobOffersByMail;
}

Promotion: @promo.GetDisplayName()

Wynik

Promocja: Wyślij oferty pracy pocztą

Todd
źródło
4
Dodaj, aby dodać następujące przestrzenie nazw: using System; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Reflection;
Peter Kerr
Świetne rozwiązanie, ale dostaję {"Szablony mogą być używane tylko z dostępem do pola, dostępem do właściwości, indeksu tablicy jednowymiarowej lub wyrażeń indeksu niestandardowego z jednym parametrem."}
Casey Crookston
Patrząc na inne odpowiedzi SO dla tego komunikatu o błędzie (nie jestem z nim zaznajomiony), wydaje się, że możesz używać tego z poziomu metody pomocniczej HTML (np. @Html.DisplayFor(m => m.myEnum.GetDisplayName()), Która nie zadziała, ponieważ oczekują, że ocenione wyrażenie da właściwość lub coś podobnego. Powinieneś użyć wartości gołego wyliczenia jak w powyższym przykładzie
Todd
7
Do wyniku dodałem zerowe sprawdzenie referencji, GetCustomAttribute<DisplayAttribute>()ponieważ dla niektórych Enums może tego nie ma. Powraca do sytuacji, enumValue.ToString()gdy DisplayAttribute nie był obecny.
H Dog
1
Użyłem tego, aby utworzyć, List<SelectListItem>który został wypełniony przez Enum ze wszystkimi indywidualnymi DisplayAttribute.Nameadnotacjami - działało idealnie, dziękuję !! 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

Na podstawie odpowiedzi Aydin za Proponuję mniej „duplicatious” Wdrożenie (ponieważ możemy łatwo uzyskać Typez Enumsamej wartości, zamiast dostarczania go jako parametr 😉:

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

EDYCJA (na podstawie komentarza @Vahagn Nahapetyan)

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

Teraz możemy go używać w bardzo czysty sposób:

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

Co skutkuje w

„Sen”

Bernoulli IT
źródło
1
Zdecydowanie najprostsza i najłatwiejsza ze wszystkich odpowiedzi. Dzięki!
Casey Crookston
Powinieneś być ostrożny z .First (). Spowoduje to wyjątek, na przykład jeśli twoje imię wyliczeniowe to „Równa się”
Vahagn Nahapetyan
Rozumiem „niebezpieczeństwo” za pomocą First (). W tym konkretnym przypadku nie wydaje się to problemem. Ponieważ jest to metoda rozszerzenia, w której thismusi być poprawną (nie zerową) wartością wyliczenia. W przeciwnym razie wywołanie metody już rzuciłoby (co jest obowiązkiem kodu wywołującego). To sprawia, że GetType()na pewno zapewni prawidłowy typ wyliczenia, w którym enumvaluena pewno będzie członkiem. Ale GetCustomAttribute może zwrócić wartość null, więc podałem nietypową wersję metody zwracającej null, gdy łańcuch wywołań metod ma gdzieś wartość null. Dzięki!
Bernoulli IT,
1
W przypadku drugiego wariantu kodu wydaje się, że po GetMember nie trzeba używać operatora warunkowego o wartości zerowej, ponieważ ta metoda zawsze zwraca tablicę MemberInfo i nigdy nie zwraca wartości null. I wydaje mi się, że lepiej jest użyć FirstOrDefault zamiast tylko First. Wtedy użycie operatora null-warunkowego po FirstOrDefault będzie spójne.
Alex34758,
28

Jeśli korzystasz z MVC 5.1 lub wyższej, istnieje prostszy i bardziej przejrzysty sposób: użyj adnotacji danych (z System.ComponentModel.DataAnnotationsprzestrzeni nazw), jak poniżej:

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

I w widoku, po prostu umieść go w odpowiednim pomocniku HTML:

@Html.EnumDropDownListFor(model => model.Color)
1_bug
źródło
@SegmentationFault dlaczego? Czy możesz opisać swój problem? Z jakiej wersji .NET / MVC korzystasz? Jaki masz błąd? Proszę być bardziej precyzyjnym.
1_bug
6
Ponieważ działa tylko w menu rozwijanych, a nie nigdzie indziej.
Błąd segmentacji
2
Wydaje się, że nie istnieje w rdzeniu .net
Lonefish,
3
.net core używa Html.GetEnumSelectList (typeof (YourEnum)) @Lonefish
Patrick Mcvay
2
jeśli chcemy użyć @ Html.DisplayFor (yourEnumField), możemy umieścić Enum.cshtml w katalogu DisplayTemplates (w katalogu współdzielonym). w tym pliku musimy umieścić tylko 2 linie. pierwszy to: „@model Enum” drugi to: „@GetDisplayName (Model)”. metoda GetDisplayName musi być taka sama jak w oprogramowaniu ansB @ bernoulli IT
programista
11

Możesz użyć metody Type.GetMember , a następnie uzyskać informacje o atrybucie za pomocą odbicia:

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

Było kilka podobnych postów tutaj:

Uzyskiwanie atrybutów wartości Enum

Jak sprawić, by MVC3 DisplayFor wyświetlał wartość atrybutu Display Enum?

maximpa
źródło
8

Opierając się na wielką odpowiedzi Todda , który zbudowany na wielką odpowiedź Aydin jest , oto rodzajowe metoda rozszerzenie, które nie wymaga żadnych parametrów typu.

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

Potrzebowałem tego do mojego projektu, ponieważ coś takiego jak poniższy kod, gdzie nie każdy członek wyliczenia ma DisplayAttribute, nie działa z rozwiązaniem Todda:

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

Jeśli jest to skomplikowane rozwiązanie prostego problemu, daj mi znać, ale to była poprawka, której użyłem.

Sinjai
źródło
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
źródło
Nie działa: / Otrzymuję błądInvalidOperationException: Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions.
Muflix
6

Mam dwa rozwiązania tego pytania.

  1. Pierwszym rozwiązaniem jest uzyskanie nazw wyświetlanych z wyliczenia.
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. Drugie rozwiązanie polega na uzyskaniu nazwy wyświetlanej z nazwy enum, ale będzie ona podzielona enum w języku programisty, zwana łatką.
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
źródło
5

W przypadku ASP.Net Core 3.0 zadziałało to dla mnie (podziękowania dla poprzednich użytkowników).

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

Klasa mojego widoku:

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

Przykład widoku brzytwy z etykietą i rozwijaną listą. Zauważ, że lista rozwijana nie wymaga metody pomocniczej:

@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>
}
Morze ocean
źródło
Dodałbym sprawdzenie zwracanego ciągu metody GetDisplayName. IssullOrEmpty (retVal)? enumValue.ToString (): retVal;
Sniipe
4

Musisz użyć odrobiny refleksji, aby uzyskać dostęp do tego atrybutu:

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;

Zalecam zawinięcie tej metody w metodę rozszerzenia lub wykonanie tego w modelu widoku.

alexn
źródło
4

Z Core 2.1

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

łączenie wszystkich skrzynek razem z góry:

  • elementy wyliczane z nazwami podstawowych elementów obiektu ( Equals,ToString )
  • opcjonalny Displayatrybut

oto mój kod:

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
źródło
Ładne rozwiązanie, które obsługuje opcjonalny atrybut wyświetlania. Dzięki!
Wellspring,
3

Przepraszam, że to zrobiłem, ale nie mogłem użyć żadnej z pozostałych odpowiedzi w obecnej postaci i nie mam czasu, aby opisać to w komentarzach.

Używa składni 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
źródło
2

Opierając się na odpowiedziach Aydina i Todda, oto metoda rozszerzenia, która pozwala również uzyskać nazwę z pliku zasobów

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

i używaj go jak

public enum Season 
{
    [Display(ResourceType = typeof(Resource), Name = Season_Summer")]
    Summer
}
Peter Kerr
źródło
Próbuję sprawić, aby działało to w moim projekcie, ale pojawia się błąd „nowy ResourceManager (zasób) .GetString (nazwa);” linia. Zadałem pytanie ( stackoverflow.com/questions/31319251/... ) i wysłano mnie tutaj. Gdy przeglądam „ResourceManager (zasób)” podczas działania, zwraca „Resources.Enums.resource”. Każda pomoc byłaby bardzo mile widziana. Dziękuję Ci!
Karinne,
Zaktualizowano kod, aby lepiej obsługiwał wartości zerowe, gdy nie masz ustawionej nazwy wyświetlanej dla niektórych wartości wyliczeniowych - może pomóc
Peter Kerr
To wciąż nie działało. Zaktualizowałem moje pytanie na stackoverflow.com/questions/31319251/... z komunikatem o błędzie. Dzięki za pomoc!
Karinne,
1

Chcę wnieść wkład w zależne od kultury rozszerzenie enum GetDisplayName. Mam nadzieję, że przyda się to każdemu, kto wcześniej przegląda tę odpowiedź:

„standardowy” sposób, o którym wspominali Aydin Adn i Todd :

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

Sposób „zależny od kultury”:

    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
źródło
1

Aktualizacja 2020: zaktualizowana wersja funkcji udostępnianej przez wiele osób w tym wątku, ale teraz od wersji C # 7.3:

Teraz możesz ograniczyć metody ogólne do typów wyliczeń, dzięki czemu możesz napisać jedno rozszerzenie metody, aby używać go ze wszystkimi wyliczeniami w następujący sposób:

Ogólna metoda rozszerzenia:

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

Wyliczenie:

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

Jak tego użyć:

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

Bonus, wyliczenia z flagami: jeśli masz do czynienia z normalnymi wyliczeniami, powyższa funkcja jest wystarczająca, ale jeśli dowolna z twoich wyliczeń może przyjmować wiele wartości za pomocą flag, musisz zmodyfikować ją w ten sposób (ten kod używa C # 8 cechy):

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

    } 

Wyliczenie z flagami:

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

Jak tego użyć:

var tipoContribuyente = TipoContribuyente.RetenedorIVA | TipoContribuyente.GranContribuyente;
var textoAux = tipoContribuyente.ATexto(); // Prints "Gran Contribuyente, Retenedor de IVA".
David
źródło
0

Na podstawie poprzednich odpowiedzi stworzyłem tego wygodnego pomocnika do obsługi wszystkich właściwości DisplayAttribute w czytelny sposób:

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
źródło
0

Próbowałem zrobić to jako edycję, ale zostało odrzucone; Nie rozumiem dlaczego.

Powyższe spowoduje zgłoszenie wyjątku, jeśli nazwiesz go Enum, który ma mieszankę niestandardowych atrybutów i zwykłych przedmiotów, np

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

Zmodyfikowałem więc nieco kod, aby sprawdzić niestandardowe atrybuty, zanim spróbuję uzyskać do nich dostęp, i użyj nazwy, jeśli nie zostaną znalezione.

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();
    }
}
Czerwony
źródło
0

Korzystając z MVC5 możesz użyć:

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
}

jeśli chcesz utworzyć rozwijany selektor, możesz użyć:

@Html.EnumDropdownListFor(expression: model => model.PromotionSelector, optionLabel: "Select") 
M.Hazara
źródło