Enum ToString с удобными для пользователя строками


283

Мое перечисление состоит из следующих значений:

private enum PublishStatusses{
    NotCompleted,
    Completed,
    Error
};

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


возможный дубликат C # String перечислений
nawfal

Возможный дубликат строкового представления Enum
Liam

Ответы:


350

Я использую Descriptionатрибут из пространства имен System.ComponentModel. Просто украсьте перечисление:

private enum PublishStatusValue
{
    [Description("Not Completed")]
    NotCompleted,
    Completed,
    Error
};

Затем используйте этот код, чтобы получить его:

public static string GetDescription<T>(this T enumerationValue)
    where T : struct
{
    Type type = enumerationValue.GetType();
    if (!type.IsEnum)
    {
        throw new ArgumentException("EnumerationValue must be of Enum type", "enumerationValue");
    }

    //Tries to find a DescriptionAttribute for a potential friendly name
    //for the enum
    MemberInfo[] memberInfo = type.GetMember(enumerationValue.ToString());
    if (memberInfo != null && memberInfo.Length > 0)
    {
        object[] attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);

        if (attrs != null && attrs.Length > 0)
        {
            //Pull out the description value
            return ((DescriptionAttribute)attrs[0]).Description;
        }
    }
    //If we have no description attribute, just return the ToString of the enum
    return enumerationValue.ToString();
}

12
Этот пример легче читать. stackoverflow.com/questions/1415140/…
RayLoveless

31
Я подозреваю, что существенное снижение производительности для использования отражения, как описано в этом решении. Код метода Уилла, использующего метод расширения ToFriendlyString, гораздо проще понять, и его производительность также должна быть чрезвычайно высокой.
humbads

1
Мне нравится версия, на которую ссылается @RayL, поскольку в Enums добавляется только метод расширения. Если это все, что вы хотите использовать для этого (как указано с помощью ArgumentException, тогда нет причин для того, чтобы метод был полностью универсальным.
krillgar

4
Это означает, что каждому перечислению нужен свой собственный метод расширения. Это более общее использование и требует больше работы, но вы, вероятно, захотите определить, что означает «быстрый», прежде чем мы определимся с производительностью.
Рэй Бойсен

2
@petar, который работает, но не, если вы хотите, чтобы дружественные строки отображались пользователям. MY_TYPE будет иметь подчеркивание и не будет настраиваться.
Рэй Бойсен

354

Я делаю это с помощью методов расширения:

public enum ErrorLevel
{
  None,
  Low,
  High,
  SoylentGreen
}

public static class ErrorLevelExtensions
{
  public static string ToFriendlyString(this ErrorLevel me)
  {
    switch(me)
    {
      case ErrorLevel.None:
        return "Everything is OK";
      case ErrorLevel.Low:
        return "SNAFU, if you know what I mean.";
      case ErrorLevel.High:
        return "Reaching TARFU levels";
      case ErrorLevel.SoylentGreen:
        return "ITS PEOPLE!!!!";
      default:
        return "Get your damn dirty hands off me you FILTHY APE!";
    }
  }
}

6
Это намного чище, чем ответ Атрибут. Ницца!
Pennyrave

3
@pennyrave: Эх. Многие компоненты пользовательского интерфейса ожидают найти и использовать DisplayNameAttribute и DescriptionAttribute. Фактически, теперь я использую их и метод расширения, чтобы легко получить эти значения.

60
Проблема, с которой я сталкиваюсь, заключается в том, что вы постоянно пишете эти методы расширения. С помощью механизма атрибутов это простой способ его украшения и вызов только одного метода.
Рэй Бойсен

5
Не уверен, что вы имеете в виду?
Рэй Бойсен

9
На мой взгляд, лучше позволить defaultреализации case возвращать me.ToString()и предоставлять операторы switch только для значений enum, которые вы хотите переопределить. В вашем примере я получаю, что все они разные, но в реальных случаях использования я подозреваю, что большинство значений перечисления из одного слова будет достаточно, и вы будете предоставлять переопределения только для значений из нескольких слов перечисления.
Скотт

78

Может быть, я что-то упустил, но что не так с Enum.GetName?

public string GetName(PublishStatusses value)
{
    return Enum.GetName(typeof(PublishStatusses), value)
}

edit: для удобных для пользователя строк вам нужно пройти через .resource, чтобы выполнить интернационализацию / локализацию, и, возможно, было бы лучше использовать фиксированный ключ, основанный на ключе enum, чем атрибут decorator для того же.


12
Я возвращаю буквальное значение enum, а не какое-то удобное для пользователя.
Борис Калленс

2
oic - ну, есть довольно большой случай, когда вам нужно пройти через библиотеку строковых ресурсов на основе этого значения, потому что альтернатива (атрибуты decorator) не будет поддерживать I18N
annakata

1
В случае I18N я бы сделал метод GetDescription () поиска в lib ресурса для переведенной строки и вернулся бы к описанию, а затем вернулся к литералу.
Борис Калленс

3
+1 для MyEnum.ToString () в качестве ключа ресурса для локализации. я делал это годами
jackvsworld

1
@annakata, мы фактически расширили механизм атрибутов, включив поддержку l18N, на самом деле это простое изменение.
Рэй Бойсен

23

Я создал метод обратного расширения, чтобы преобразовать описание обратно в значение перечисления:

public static T ToEnumValue<T>(this string enumerationDescription) where T : struct
{
    var type = typeof(T);

    if (!type.IsEnum)
        throw new ArgumentException("ToEnumValue<T>(): Must be of enum type", "T");

    foreach (object val in System.Enum.GetValues(type))
        if (val.GetDescription<T>() == enumerationDescription)
            return (T)val;

    throw new ArgumentException("ToEnumValue<T>(): Invalid description for enum " + type.Name, "enumerationDescription");
}

15
Извините, но спасибо за попытку помочь! Хотя, поскольку это сайт вопросов и ответов, ответы должны быть попыткой прямо ответить на вопрос. И вопрос конкретно гласит: « Мне не нужно снова переходить от строки к значению ». Еще раз спасибо!
Джесси

8
Спасибо за положительную критику. Всегда трудно быть новым для сайта и узнавать о его культуре и нюансах. Я рад, что есть такие люди, как вы, которые устанавливают новых парней прямо. Еще раз спасибо за то, что не бросили нового парня.
Брайан Ричардсон

6
@Jesse И через 4 года кто-то с радостью найдет здесь код Бьрихардсона! SO может быть сайтом вопросов и ответов, но это не значит, что после ответа на вопросы они не работают.
Джон

18

Самым простым решением здесь является использование пользовательского метода расширения (по крайней мере, в .NET 3.5 - вы можете просто преобразовать его в статический вспомогательный метод для более ранних версий фреймворка).

public static string ToCustomString(this PublishStatusses value)
{
    switch(value)
    {
        // Return string depending on value.
    }
    return null;
}

Здесь я предполагаю, что вы хотите вернуть что-то кроме фактического имени значения перечисления (которое вы можете получить, просто вызвав ToString).


Несмотря на действительность, мне больше нравится атрибут. Таким образом, я могу поместить свой метод toSTring в отдельную библиотеку, в то время как пользовательское строковое представление помещается вместе с самим перечислением
Boris Callens

1
Справедливо. Я предполагаю, что одним из преимуществ этого метода является то, что вы можете включить в метод аргумент, указывающий некоторую переменную состояния, а затем изменить, какое строковое представление возвращается в зависимости от этого.
Нолдорин

1
Да, все зависит от объема метода, я думаю. В то время как способ атрибутов является более общим, ваше решение более локализовано. В конечном итоге все зависит от потребностей.
Борис Калленс

1
Вы можете разместить методы расширения где угодно. Вы просто должны ссылаться на него, где вы хотите их использовать.

Да, но это будет означать, что этот метод расширения следует переписывать каждый раз, когда вы вводите новое перечисление, для которого вы хотите иметь понятное имя. Это также означало бы, что ВСЕ ваши приложения будут содержать дружественные имена для ВСЕХ ваших других приложений ...
Борис Калленс

13

Этот другой пост - Java. Вы не можете поместить методы в Enums в C #.

просто сделайте что-то вроде этого:

PublishStatusses status = ...
String s = status.ToString();

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


3
toString не является безопасным во всех случаях - перечисление с несколькими записями с одним и тем же значением (скажем, для целочисленных перечислений) вернет ключ первого совпадающего значения, а не ключ проверенного элемента, поэтому предпочтительным является Enum.GetName
annakata

4
Ну, это было самое простое решение для его конкретного перечисления
Лемми

9

Самый простой способ - просто включить этот класс расширения в ваш проект, он будет работать с любым перечислением в проекте:

public static class EnumExtensions
{
    public static string ToFriendlyString(this Enum code)
    {
        return Enum.GetName(code.GetType(), code);
    }
}

Использование:

enum ExampleEnum
{
    Demo = 0,
    Test = 1, 
    Live = 2
}

...

ExampleEnum ee = ExampleEnum.Live;
Console.WriteLine(ee.ToFriendlyString());

2
Это загадка, почему этот комментарий не является принятым или большинством голосов - без размышлений, без лишних атрибутов, идеально подходит для простых ситуаций, когда перечисление уже хорошо названо. Вы можете сделать этот ответ еще дальше и добавить пробелы между заглавными буквами перед возвратом «My Enum».
Vix

12
Если перечисление уже имеет правильное имя, нет необходимости в каком-либо методе расширения. Просто используйте существующий метод ToString (). string result = "Result: " + ee;
Джон

Это должен быть лучший ответ. Это работает для любого перечисления. Вы даже можете реализовать его с помощью специального Enum, просто изменив тип параметра Enum на фактический Enum, для которого он будет использоваться.
Хуану Хаэдо

6
Этот ответ и все комментарии игнорируют первоначальный запрос расширенного описания. Вы, ребята, полностью пропустили упражнение, которое должно вернуть что-то, отличное от значения ToString по умолчанию. Я не буду опускать все примечания к этому ответу здесь, но я конечно хочу.
TonyG

8

Некоторые другие более примитивные опции, которые избегают классов / ссылочных типов:

  • Метод массива
  • Метод вложенных структур

Метод массива

private struct PublishStatusses
{
    public static string[] Desc = {
        "Not Completed",
        "Completed",
        "Error"
    };

    public enum Id
    {
        NotCompleted = 0,
        Completed,
        Error
    };
}

использование

string desc = PublishStatusses.Desc[(int)PublishStatusses.Id.Completed];

Метод вложенных структур

private struct PublishStatusses
{
    public struct NotCompleted
    {
        public const int Id = 0;
        public const string Desc = "Not Completed";
    }

    public struct Completed
    {
        public const int Id = 1;
        public const string Desc = "Completed";
    }

    public struct Error
    {
        public const int Id = 2;
        public const string Desc = "Error";
    }            
}

использование

int id = PublishStatusses.NotCompleted.Id;
string desc = PublishStatusses.NotCompleted.Desc;

Обновление (03/09/2018)

Гибрид методов расширения и первый метод выше.

Я предпочитаю, чтобы перечисления определялись там, где они «принадлежат» (ближе к источнику происхождения, а не в каком-то общем глобальном пространстве имен).

namespace ViewModels
{
    public class RecordVM
    {
        //public enum Enum { Minutes, Hours }
        public struct Enum
        {
            public enum Id { Minutes, Hours }
            public static string[] Name = { "Minute(s)", "Hour(s)" };
        }
    }
}

Метод расширения кажется подходящим для общей области, и «локализованное» определение перечисления теперь делает метод расширения более многословным.

namespace Common
{
    public static class EnumExtensions
    {
        public static string Name(this RecordVM.Enum.Id id)
        {
            return RecordVM.Enum.Name[(int)id];
        }
    }   
}

Пример использования перечисления и его метода расширения.

namespace Views
{
    public class RecordView 
    {
        private RecordDataFieldList<string, string> _fieldUnit;

        public RecordView()
        {
            _fieldUnit.List = new IdValueList<string, string>
            {            
                new ListItem<string>((int)RecordVM.Enum.Id.Minutes, RecordVM.Enum.Id.Minutes.Name()),
                new ListItem<string>((int)RecordVM.Enum.Id.Hours, RecordVM.Enum.Id.Hours.Name())
            };
        }

        private void Update()
        {    
            RecordVM.Enum.Id eId = DetermineUnit();

            _fieldUnit.Input.Text = _fieldUnit.List.SetSelected((int)eId).Value;
        }
    }
}

Примечание: я на самом деле решил исключить Enumоболочку (и Nameмассив), так как лучше, чтобы строки имен исходили из ресурса (т.е. файла конфигурации или БД), а не были жестко запрограммированы, и потому что я в конечном итоге поместил метод расширения в ViewModelsпространство имен (просто в другом файле "CommonVM.cs"). Плюс все .Idэто становится отвлекающим и громоздким.

namespace ViewModels
{
    public class RecordVM
    {
        public enum Enum { Minutes, Hours }
        //public struct Enum
        //{
        //    public enum Id { Minutes, Hours }
        //    public static string[] Name = { "Minute(s)", "Hour(s)" };
        //}
    }
}

CommonVM.cs

//namespace Common
namespace ViewModels
{
    public static class EnumExtensions
    {
        public static string Name(this RecordVM.Enum id)
        {
            //return RecordVM.Enum.Name[(int)id];
            switch (id)
            {
                case RecordVM.Enum.Minutes: return "Minute(s)";                    
                case RecordVM.Enum.Hours: return "Hour(s)";
                default: return null;
            }
        }
    }   
}

Пример использования перечисления и его метода расширения.

namespace Views
{
    public class RecordView 
    {
        private RecordDataFieldList<string, string> _fieldUnit

        public RecordView()
        {
            _fieldUnit.List = new IdValueList<string, string>
            {            
                new ListItem<string>((int)RecordVM.Enum.Id.Minutes, RecordVM.Enum.Id.Minutes.Name()),
                new ListItem<string>((int)RecordVM.Enum.Id.Hours, RecordVM.Enum.Id.Hours.Name())
            };
        }

        private void Update()
        {    
            RecordVM.Enum eId = DetermineUnit();

            _fieldUnit.Input.Text = _fieldUnit.List.SetSelected((int)eId).Value;
        }
    }
}

+ 1-1 = 0 голосов: это решение сохраняет синтаксис Enum и элегантно решает проблему без отражения или сложного кода, поэтому +1 там. Но он теряет черты самих Enums. Так что, хотя IMO это хороший вариант, он не отвечает на реальный вопрос и получает -1. Net 0. Извините, у нас нет способа записать это лучше в SO.
TonyG

@ TonyG Достаточно справедливо. Пропустив несколько вопросов по оценке навыков .net на pluarlsight.com, я начал понимать, как обстоят дела с C # enum, поэтому, вероятно, неплохо бы хотя бы узнать их возможности, прежде чем решать, какую методологию применять (особенно для повсеместного использования, рефакторинга. может быть немного времени;).
Сами

7

Вы можете использовать HUMANIZER пакет с гуманизацией Enums Possiblity. Пример:

enum PublishStatusses
{
    [Description("Custom description")]
    NotCompleted,
    AlmostCompleted,
    Error
};

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

var st1 = PublishStatusses.NotCompleted;
var str1 = st1.Humanize(); // will result in Custom description

var st2 = PublishStatusses.AlmostCompleted;
var str2 = st2.Humanize(); // will result in Almost completed (calculated automaticaly)

Он также использует отражение и не кэшируется. github.com/Humanizr/Humanizer/blob/…
Конрад

Это будет так же медленно, как решение в первом ответе Рэя
Конрад

5

Что касается Ray Booysen, в коде есть ошибка: Enum ToString с удобными для пользователя строками

Вам необходимо учитывать несколько атрибутов в значениях перечисления.

public static string GetDescription<T>(this object enumerationValue)
            where T : struct
    {
        Type type = enumerationValue.GetType();
        if (!type.IsEnum)
        {
            throw new ArgumentException("EnumerationValue must be of Enum type", "enumerationValue");
        }

        //Tries to find a DescriptionAttribute for a potential friendly name
        //for the enum
        MemberInfo[] memberInfo = type.GetMember(enumerationValue.ToString());
        if (memberInfo != null && memberInfo.Length > 0)
        {
            object[] attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);

            if (attrs != null && attrs.Length > 0 && attrs.Where(t => t.GetType() == typeof(DescriptionAttribute)).FirstOrDefault() != null)
            {
                //Pull out the description value
                return ((DescriptionAttribute)attrs.Where(t=>t.GetType() == typeof(DescriptionAttribute)).FirstOrDefault()).Description;
            }
        }
        //If we have no description attribute, just return the ToString of the enum
        return enumerationValue.ToString();

4
Пропуск проверки для нескольких атрибутов Description является намеренным. Если в enum их два, и вы используете для создания описания, я бы хотел думать, что это исключительное обстоятельство. Я думаю, что настоящая ошибка заключается в том, что я не делаю Single () для исключения. Иначе вся подпись метода не имеет смысла. GetDescription ()? Какое описание? Совокупность?
Рэй Бойсен

4
public enum MyEnum
{
    [Description("Option One")]
    Option_One
}

public static string ToDescriptionString(this Enum This)
{
    Type type = This.GetType();

    string name = Enum.GetName(type, This);

    MemberInfo member = type.GetMembers()
        .Where(w => w.Name == name)
        .FirstOrDefault();

    DescriptionAttribute attribute = member != null
        ? member.GetCustomAttributes(true)
            .Where(w => w.GetType() == typeof(DescriptionAttribute))
            .FirstOrDefault() as DescriptionAttribute
        : null;

    return attribute != null ? attribute.Description : name;
}

3
Всегда приятно написать текст, объясняющий, почему это должно работать, а почему - нет.
Фаберест

Просто FYI, соглашения C # кода хотят локальные переменные и параметры метода с заглавной буквой. Единственным исключением является thisпараметр в методах расширения, который вы можете увидеть Thisво многих примерах в Интернете. Называя его как его тип как вы ( Enum Enum), вы делаете код менее читабельным.
Массимилиано Краус

4

Вместо использования перечисления используйте статический класс.

замещать

private enum PublishStatuses{
    NotCompleted,
    Completed,
    Error
};

с участием

private static class PublishStatuses{
    public static readonly string NotCompleted = "Not Completed";
    public static readonly string Completed = "Completed";
    public static readonly string Error = "Error";
};

это будет использоваться так

PublishStatuses.NotCompleted; // "Not Completed"

Проблема с использованием лучших «методов расширения» решения:

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


2

Я являюсь поклонником VB.NET, так что вот моя версия, сочетающая метод DescriptionAttribute с методом расширения. Во-первых, результаты:

Imports System.ComponentModel ' For <Description>

Module Module1
  ''' <summary>
  ''' An Enum type with three values and descriptions
  ''' </summary>
  Public Enum EnumType
    <Description("One")>
    V1 = 1

    ' This one has no description
    V2 = 2

    <Description("Three")>
    V3 = 3
  End Enum

  Sub Main()
    ' Description method is an extension in EnumExtensions
    For Each v As EnumType In [Enum].GetValues(GetType(EnumType))
      Console.WriteLine("Enum {0} has value {1} and description {2}",
        v,
        CInt(v),
        v.Description
      )
    Next
    ' Output:
    ' Enum V1 has value 1 and description One
    ' Enum V2 has value 2 and description V2
    ' Enum V3 has value 3 and description Three
  End Sub
End Module

Основные вещи: перечисление с именем EnumType с тремя значениями V1, V2 и V3. «Волшебство» происходит в вызове Console.WriteLine в Sub Main (), где последний аргумент - просто v.Description. Это возвращает «Один» для V1, «V2» для V2 и «Три» для V3. Этот Description-метод на самом деле является методом расширения, определенным в другом модуле EnumExtensions:

Option Strict On
Option Explicit On
Option Infer Off

Imports System.Runtime.CompilerServices
Imports System.Reflection
Imports System.ComponentModel

Module EnumExtensions
  Private _Descriptions As New Dictionary(Of String, String)

  ''' <summary>
  ''' This extension method adds a Description method
  ''' to all enum members. The result of the method is the
  ''' value of the Description attribute if present, else
  ''' the normal ToString() representation of the enum value.
  ''' </summary>
  <Extension>
  Public Function Description(e As [Enum]) As String
    ' Get the type of the enum
    Dim enumType As Type = e.GetType()
    ' Get the name of the enum value
    Dim name As String = e.ToString()

    ' Construct a full name for this enum value
    Dim fullName As String = enumType.FullName + "." + name

    ' See if we have looked it up earlier
    Dim enumDescription As String = Nothing
    If _Descriptions.TryGetValue(fullName, enumDescription) Then
      ' Yes we have - return previous value
      Return enumDescription
    End If

    ' Find the value of the Description attribute on this enum value
    Dim members As MemberInfo() = enumType.GetMember(name)
    If members IsNot Nothing AndAlso members.Length > 0 Then
      Dim descriptions() As Object = members(0).GetCustomAttributes(GetType(DescriptionAttribute), False)
      If descriptions IsNot Nothing AndAlso descriptions.Length > 0 Then
        ' Set name to description found
        name = DirectCast(descriptions(0), DescriptionAttribute).Description
      End If
    End If

    ' Save the name in the dictionary:
    _Descriptions.Add(fullName, name)

    ' Return the name
    Return name
  End Function
End Module

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

(Извините за решение VB.NET - должно быть относительно просто перевести его на C #, а мой C # ржавеет по новым темам, таким как расширения)


2

Очистите резюме вышеупомянутых предложений с образцом:

namespace EnumExtensions {

using System;
using System.Reflection;

public class TextAttribute : Attribute {
   public string Text;
   public TextAttribute( string text ) {
      Text = text;
   }//ctor
}// class TextAttribute

public static class EnumExtender {

public static string ToText( this Enum enumeration ) {

   MemberInfo[] memberInfo = enumeration.GetType().GetMember( enumeration.ToString() );

   if ( memberInfo != null && memberInfo.Length > 0 ) {

      object[] attributes = memberInfo[ 0 ].GetCustomAttributes( typeof(TextAttribute),  false );

      if ( attributes != null && attributes.Length > 0 ) {
         return ( (TextAttribute)attributes[ 0 ] ).Text;
      }

   }//if

   return enumeration.ToString();

}//ToText

}//class EnumExtender

}//namespace

ИСПОЛЬЗОВАНИЕ:

using System;
using EnumExtensions;

class Program {

public enum Appearance {

  [Text( "left-handed" ) ]
  Left,

  [Text( "right-handed" ) ]
  Right,

}//enum

static void Main( string[] args ) {

   var appearance = Appearance.Left;
   Console.WriteLine( appearance.ToText() );

}//Main

}//class

1
В C # есть атрибут [Description ("")], почему бы не использовать это?
Стефан Коенен

Конечно, использование [Description ("")] - это путь. Но я хотел, чтобы образец был полным.
подчеркиваем

2

Используйте Enum.GetName

Из приведенной выше ссылки ...

using System;

public class GetNameTest {
    enum Colors { Red, Green, Blue, Yellow };
    enum Styles { Plaid, Striped, Tartan, Corduroy };

    public static void Main() {

        Console.WriteLine("The 4th value of the Colors Enum is {0}", Enum.GetName(typeof(Colors), 3));
        Console.WriteLine("The 4th value of the Styles Enum is {0}", Enum.GetName(typeof(Styles), 3));
    }
}
// The example displays the following output:
//       The 4th value of the Colors Enum is Yellow
//       The 4th value of the Styles Enum is Corduroy

2

Согласно этой документации: https://docs.microsoft.com/pt-br/dotnet/api/system.enum.tostring?view=netframework-4.8

Можно просто преобразовать перечислитель в строку, используя такой формат:

public enum Example
{
    Example1,
    Example2
}

Console.WriteLine(Example.Example1.ToString("g"));

//Outputs: "Example1"

Вы можете увидеть все возможные форматы по этой ссылке: https://docs.microsoft.com/pt-br/dotnet/api/system.string?view=netframework-4.8


1

Это обновление кода Рэя Бойзена, в котором используется общий метод GetCustomAttributes и LINQ для упрощения работы.

    /// <summary>
    /// Gets the value of the <see cref="T:System.ComponentModel.DescriptionAttribute"/> on an struct, including enums.  
    /// </summary>
    /// <typeparam name="T">The type of the struct.</typeparam>
    /// <param name="enumerationValue">A value of type <see cref="T:System.Enum"/></param>
    /// <returns>If the struct has a Description attribute, this method returns the description.  Otherwise it just calls ToString() on the struct.</returns>
    /// <remarks>Based on http://stackoverflow.com/questions/479410/enum-tostring/479417#479417, but useful for any struct.</remarks>
    public static string GetDescription<T>(this T enumerationValue) where T : struct
    {
        return enumerationValue.GetType().GetMember(enumerationValue.ToString())
                .SelectMany(mi => mi.GetCustomAttributes<DescriptionAttribute>(false),
                    (mi, ca) => ca.Description)
                .FirstOrDefault() ?? enumerationValue.ToString();
    }   

Не понимая, зачем вам это нужно, чтобы быть универсальным? Если вы собираетесь использовать отражение?
Ли Лувьер

@LeeLouviere Главным образом, чтобы избежать коробок, когда структура (тип значения) передается в качестве параметра.
Ричард Энтони Хейн

1
вместо NumberrationValue.GetType () используйте: typeof (T).
Слава

1
Огромное улучшение в одну строку по сравнению с принятым ответом без потери читаемости (YMMV). Да, с typeof (T).
TonyG

1

Еще чище резюме:

using System;
using System.Reflection;

public class TextAttribute : Attribute
{
    public string Text;
    public TextAttribute(string text)
    {
        Text = text;
    }
}  

public static class EnumExtender
{
    public static string ToText(this Enum enumeration)
    {
        var memberInfo = enumeration.GetType().GetMember(enumeration.ToString());
        if (memberInfo.Length <= 0) return enumeration.ToString();

        var attributes = memberInfo[0].GetCustomAttributes(typeof(TextAttribute), false);
        return attributes.Length > 0 ? ((TextAttribute)attributes[0]).Text : enumeration.ToString();
    }
}

Такое же использование, как подчеркивание.


0

Для флагов enum в том числе.

    public static string Description(this Enum value)
    {
        Type type = value.GetType();

        List<string> res = new List<string>();
        var arrValue = value.ToString().Split(',').Select(v=>v.Trim());
        foreach (string strValue in arrValue)
        {
            MemberInfo[] memberInfo = type.GetMember(strValue);
            if (memberInfo != null && memberInfo.Length > 0)
            {
                object[] attrs = memberInfo[0].GetCustomAttributes(typeof(DescriptionAttribute), false);

                if (attrs != null && attrs.Length > 0 && attrs.Where(t => t.GetType() == typeof(DescriptionAttribute)).FirstOrDefault() != null)
                {
                    res.Add(((DescriptionAttribute)attrs.Where(t => t.GetType() == typeof(DescriptionAttribute)).FirstOrDefault()).Description);
                }
                else
                    res.Add(strValue);
            }
            else
                res.Add(strValue);
        }

        return res.Aggregate((s,v)=>s+", "+v);
    }

0

Если вы просто хотите добавить пробел между словами, это так же просто, как

string res = Regex.Replace(PublishStatusses.NotCompleted, "[A-Z]", " $0").Trim();

0

Я использую универсальный класс для хранения пар enum / description и вложенный вспомогательный класс для получения описания.

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

enum Status { Success, Fail, Pending }

Общий класс:

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

public class EnumX<T> where T : struct
{
    public T Code { get; set; }
    public string Description { get; set; }

    public EnumX(T code, string desc)
    {
        if (!typeof(T).IsEnum) throw new NotImplementedException();

        Code = code;
        Description = desc;
    }

    public class Helper
    {
        private List<EnumX<T>> codes;

        public Helper(List<EnumX<T>> codes)
        {
            this.codes = codes;
        }

        public string GetDescription(T code)
        {
            EnumX<T> e = codes.Where(c => c.Code.Equals(code)).FirstOrDefault();
            return e is null ? "Undefined" : e.Description;
        }
    }
}

Использование:

EnumX<Status>.Helper StatusCodes = new EnumX<Status>.Helper(new List<EnumX<Status>>()
        {
            new EnumX<Status>(Status.Success,"Operation was successful"),
            new EnumX<Status>(Status.Fail,"Operation failed"),
            new EnumX<Status>(Status.Pending,"Operation not complete. Please wait...")
        });

        Console.WriteLine(StatusCodes.GetDescription(Status.Pending));

-2

Я думаю, что лучший (и самый простой) способ решить вашу проблему - написать Extension-Method для вашего перечисления:

public static string GetUserFriendlyString(this PublishStatusses status)
    {

    }

1
Кто-то был 7 лет назад, чтобы заявить, что
Стивен

-3

Если вы хотите что-то полностью настраиваемое, попробуйте мое решение здесь:

http://www.kevinwilliampang.com/post/Mapping-Enums-To-Strings-and-Strings-to-Enums-in-NET.aspx

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


1
И сейчас сайт не работает.
Ник Фостер
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.