Как получить атрибут отображаемого имени члена Enum с помощью бритвенного кода MVC?

211

В моей модели есть свойство «Поощрение», типом которого является перечисление флага «UserPromotion». Члены моего enum имеют атрибуты отображения, установленные следующим образом:

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

Теперь я хочу иметь возможность создать, скажем, ul на мой взгляд, чтобы показать выбранные значения моего свойства «Продвижение». Это то, что я сделал до сих пор, но проблема в том, как я могу получить отображаемые имена здесь?

<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
источник
12
MVC5 поддерживает атрибут DisplayName для перечислений.
Барт Каликсто
10
Чтобы быть понятнее: только System.ComponentModel.DataAnnotations.DisplayAttribute. Не System.ComponentModel.DisplayNameAttribute.
kamranicus
1
Включает ли это использование отражения и влияет ли это на производительность? потому что это будет называться много времени.
Нико

Ответы:

182

ОБНОВИТЬ

Первое решение было сосредоточено на получении отображаемых имен из enum. Код ниже должен быть точным решением вашей проблемы.

Вы можете использовать этот вспомогательный класс для перечислений:

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

И тогда вы можете использовать его на ваш взгляд следующим образом:

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

Надеюсь, поможет! :)

Хрвое Станишич
источник
8
Все ответы используют .ToString, но от stackoverflow.com/q/483794/179311 , он говорит, чтобы использовать Enum.GetNameвместо.
bradlis7
value.GetType (). GetField (value.ToString ()) был именно тем, что я искал!
cdie
Этот ответ подходит для некоторой дополнительной проверки нуля, но если вы не используете dotfuscation, ответ на stackoverflow.com/a/4412730/852806 кажется более простым.
HockeyJ,
5
В GetDisplayValueвас должен первый тест , descriptionAttributes == nullпрежде чем пытаться получить доступ к массиву: descriptionAttributes[0]. В противном случае вы можете вызвать исключение, и строка ниже, где вы проверяете нулевое значение, никогда не будет истинной.
Роберт С.
Я бы предложил незначительные изменения: общедоступный статический IList <T> GetValues ​​(значение Enum) может быть общедоступным статическим IList <T> GetValues ​​(значение T). EnumHelper <T> to => открытый статический класс EnumHelper <T>, где T: struct, IConvertible. Может быть статический конструктор? static EnumHelper () {if (! typeof (T) .IsEnum) {throw new ArgumentException ("T должен быть перечислимым типом"); }}
Том
172

Один вкладыш - Свободный синтаксис

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

пример

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

Вывод

Какой сезон это?
Это лето

Айдын
источник
2
Не существует определения GetCustomAttribute
Тито
3
@Tito гарантирует, что ваш проект нацелен .NET Framework 4.5и что вы включаете следующие пространства именSystem.Net System.ComponentModel.DataAnnotations
Aydin
8
using System.Reflection; using System.ComponentModel.DataAnnotations; Был нужен для меня.
грешил лолвут
1
какое ужасное соглашение об именах!
любопытный мальчик
@curiousBoy Какое GetAttribute<TAttribute>ужасное соглашение об именах? Он извлекает указанный вами атрибут и использует регистр паскаля, как и все открытые методы.
Айдын
137

Основываясь на отличном ответе Айдына , вот метод расширения, который не требует никаких параметров типа.

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

ПРИМЕЧАНИЕ. GetName () следует использовать вместо свойства Name. Это гарантирует, что локализованная строка будет возвращена при использовании свойства атрибута ResourceType.

пример

Чтобы использовать его, просто укажите значение enum в вашем представлении.

@{
    UserPromotion promo = UserPromotion.SendJobOffersByMail;
}

Promotion: @promo.GetDisplayName()

Вывод

Промоушен: отправка вакансий по почте

Тодд
источник
4
Не забудьте добавить следующие пространства имен: using System; using System.ComponentModel.DataAnnotations; использование System.Linq; using System.Reflection;
Питер Керр
Отличное решение, но я получаю {"Шаблоны могут использоваться только с доступом к полям, доступом к свойствам, индексом одномерного массива или выражениями настраиваемого индексатора с одним параметром."}
Кейси Крукстон,
Глядя на другие ответы SO для этого сообщения об ошибке (я не знаком с ним), кажется, что вы могли бы использовать это из вспомогательного метода Html (например @Html.DisplayFor(m => m.myEnum.GetDisplayName()), который не будет работать, потому что они ожидают, что вычисленное выражение выдаст свойство или что-то подобное. Вы должны использовать значение перечисления чуть-чуть, как в примере выше.
Тодд
7
Я добавил проверку нулевой ссылки к результату, GetCustomAttribute<DisplayAttribute>()потому что для некоторых Enums это может отсутствовать. Это возвращается к, enumValue.ToString()если DisplayAttribute не присутствовал.
H Dog
1
Я использовал это для создания List<SelectListItem>объекта Enum со всеми индивидуальными DisplayAttribute.Nameаннотациями - это сработало отлично, спасибо! 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() }); }
Хоппер
61

Основываясь на ответе Айдына, я бы предложил менее «дублирующую» реализацию (потому что мы могли бы легко получить Typeсамо Enumзначение вместо предоставления его в качестве параметра parameter:

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

РЕДАКТИРОВАТЬ (на основе комментария @Vahagn Nahapetyan)

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

Теперь мы можем использовать его очень чисто:

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

Что приводит к

"Мечта"

Бернулли ИТ
источник
1
Безусловно самый простой и легкий из всех ответов. Спасибо!
Кейси Крукстон
Вы должны быть осторожны с .First (). Это вызовет исключение, например, если ваше имя перечисления равно «Equals»
Ваагн Нахапетян
Я понимаю «опасность» с First (). В данном конкретном случае это не кажется проблемой. Потому что это метод расширения, где thisдолжно быть допустимое (не нулевое) значение Enum. В противном случае вызов метода уже приведет к выбросу (что является обязанностью вызывающего кода). Это GetType()обязательно обеспечит правильный тип Enum, в котором enumvalueнаверняка будет член. Но GetCustomAttribute может возвращать нулевое значение, поэтому я предоставил неисключительную версию метода для возврата нулевого значения, когда цепочка вызовов методов где-либо имеет нулевое возвращаемое значение. Спасибо!
Бернулли ИТ
1
Для второго варианта вашего кода, похоже, нет необходимости использовать условный оператор null после GetMember, потому что этот метод всегда возвращает массив MemberInfo и никогда не возвращает ноль. И мне кажется, что лучше использовать FirstOrDefault, а не просто First. Тогда использование null-условного оператора после FirstOrDefault будет выглядеть последовательным.
Alex34758
28

Если вы используете MVC 5.1 или выше, есть более простой и понятный способ: просто используйте аннотацию данных (из System.ComponentModel.DataAnnotationsпространства имен), как показано ниже:

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

И, видимо, просто поместите его в правильный помощник HTML:

@Html.EnumDropDownListFor(model => model.Color)
1_bug
источник
@ Сегментация Почему? Можете ли вы описать вашу проблему? Какую версию .NET / MVC вы используете? Какую ошибку вы получили? Пожалуйста, будьте более конкретны.
1_баг
6
Потому что это работает только для выпадающих, а не где-либо еще.
Ошибка сегментации
2
Кажется, не существует в ядре .net
Lonefish
3
Ядро .net использует Html.GetEnumSelectList (typeof (YourEnum)) @Lonefish
Патрик Маквей
2
если мы хотим использовать @ Html.DisplayFor (yourEnumField), мы можем поместить Enum.cshtml в каталог DisplayTemplates (в общем каталоге). в этом файле нам нужно поместить всего 2 строки. первый: «@model Enum», второй: «@GetDisplayName (Model)». метод GetDisplayName должен быть таким же, как в @Bernoulli IT answare
разработчик,
11

Вы можете использовать метод Type.GetMember , а затем получить информацию об атрибуте, используя отражение:

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

Здесь было несколько похожих постов:

Получение атрибутов значения Enum

Как заставить MVC3 DisplayFor показывать значение атрибута отображения Enum?

maximpa
источник
8

Основываясь на великолепном ответе Тодда, основанном на отличном ответе Айдына , вот общий метод расширения, который не требует никаких параметров типа.

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

Мне это нужно для моего проекта, потому что что-то вроде приведенного ниже кода, где не каждый член перечисления имеет DisplayAttribute, не работает с решением Тодда:

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

Если это сложное решение простой проблемы, пожалуйста, дайте мне знать, но это было исправление, которое я использовал.

Sinjai
источник
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>
Дмитро
источник
Не работает: / Я получаю ошибкуInvalidOperationException: Templates can be used only with field access, property access, single-dimension array index, or single-parameter custom indexer expressions.
Muflix
6

У меня есть два решения этого вопроса.

  1. Первое решение заключается в получении отображаемых имен из 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. Второе решение заключается в получении отображаемого имени из enum name, но это будет разделение enum на языке разработчика, которое называется 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>
Ясин Суннит
источник
5

Для ASP.Net Core 3.0 это сработало для меня (благодарность предыдущим авторам).

Мой 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;
    }
}

Мой вид модели класса:

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

Пример вида бритвы с отображением метки и раскрывающегося списка. Обратите внимание, что раскрывающийся список не требует вспомогательного метода:

@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>
}
Sea_Ocean
источник
Я бы добавил проверку метода GetDisplayName, возвращающего строку. IsNullOrEmpty (retVal)? enumValue.ToString (): retVal;
Сниипе
4

Вам нужно использовать немного отражения для доступа к этому атрибуту:

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;

Я рекомендую обернуть этот метод в метод расширения или выполнить его в модели представления.

AlexN
источник
4

С Core 2.1,

public static string GetDisplayName(Enum enumValue)
{
  return enumValue.GetType()?
 .GetMember(enumValue.ToString())?[0]?
 .GetCustomAttribute<DisplayAttribute>()?
 .Name;
}
Дениз Айдын
источник
4

объединяя все граничные случаи сверху:

  • перечисление членов с именами членов базового объекта ( Equals, ToString)
  • необязательный Displayатрибут

вот мой код:

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
источник
Хорошее решение, которое обрабатывает необязательный атрибут Display. Спасибо!
Wellspring
3

Мне жаль это делать, но я не мог использовать другие ответы как есть и не успел выяснить это в комментариях.

Использует синтаксис 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>();
}
Рубен Бартелинк
источник
2

Основываясь на ответах Айдына и Тодда, вот метод расширения, который также позволяет вам получить имя из файла ресурсов.

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

и использовать его как

public enum Season 
{
    [Display(ResourceType = typeof(Resource), Name = Season_Summer")]
    Summer
}
Питер Керр
источник
Я пытаюсь заставить это работать для моего проекта, но я получаю ошибку с "новым ResourceManager (resource) .GetString (name);" линия. Я задал вопрос ( stackoverflow.com/questions/31319251/… ), и меня отправили сюда. Когда я вижу «ResourceManager (resource)» во время работы, он возвращает «Resources.Enums.resource». Любая помощь будет принята с благодарностью. Спасибо!
Карин
Обновлен код, чтобы лучше обрабатывать значения NULL, если для некоторых значений перечисления не задано отображаемое имя - это может помочь
Питер Керр
Это все еще не сработало. Я обновил свой вопрос на stackoverflow.com/questions/31319251/… с сообщением об ошибке. Спасибо за помощь!
Карин
1

Я хочу внести свой вклад с зависимым от культуры расширением перечисления GetDisplayName. Надеюсь, что это будет полезно для тех, кто ищет этот ответ, как я ранее:

«Стандартный» путь, как отметили Айдын Адн и Тодд :

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

«Культурно-зависимый» способ:

    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();
    }
Павел
источник
1

Обновление 2020: обновленная версия функции, предоставленной многими в этой теме, но теперь для C # 7.3 и далее:

Теперь вы можете ограничить универсальные методы перечислениями, чтобы вы могли написать единственное расширение метода, чтобы использовать его со всеми перечислениями следующим образом:

Общий метод расширения:

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

Перечисление:

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

Как это использовать:

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

Бонус, перечисления с флагами: если вы имеете дело с обычными перечислениями, вышеуказанной функции достаточно, но если любое из ваших перечислений может принимать несколько значений с использованием флагов, вам нужно будет изменить его следующим образом (этот код использует C # 8 функции):

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

    } 

Перечисление с флагами:

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

Как это использовать:

var tipoContribuyente = TipoContribuyente.RetenedorIVA | TipoContribuyente.GranContribuyente;
var textoAux = tipoContribuyente.ATexto(); // Prints "Gran Contribuyente, Retenedor de IVA".
Дэвид
источник
0

На основе предыдущих ответов я создал этот удобный помощник для поддержки всех свойств DisplayAttribute в удобочитаемой форме:

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
источник
0

Я пытался сделать это как редактирование, но оно было отклонено; Я не понимаю почему.

Выше приведено исключение, если вы вызываете его с помощью Enum, который имеет сочетание пользовательских атрибутов и простых элементов, например

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

Поэтому я немного изменил код, чтобы проверять наличие пользовательских атрибутов, прежде чем пытаться получить к ним доступ, и использовать имя, если ни одного не найдено.

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();
    }
}
красный
источник
0

Используя MVC5 вы можете использовать:

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
}

тогда, если вы хотите создать выпадающий селектор, вы можете использовать:

@Html.EnumDropdownListFor(expression: model => model.PromotionSelector, optionLabel: "Select") 
M.Hazara
источник