Enum ToString с удобными для пользователя строками - PullRequest
256 голосов
/ 26 января 2009

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

private enum PublishStatusses{
    NotCompleted,
    Completed,
    Error
};

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

Ответы [ 19 ]

320 голосов
/ 26 января 2009

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

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!";
    }
  }
}
319 голосов
/ 26 января 2009

Я использую атрибут 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();
}
64 голосов
/ 26 января 2009

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

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

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

18 голосов
/ 18 апреля 2013

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

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");
}
17 голосов
/ 26 января 2009

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

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

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

12 голосов
/ 26 января 2009

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

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

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

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

8 голосов
/ 28 июля 2017

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

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

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

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

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

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

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

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;
        }
    }
}
8 голосов
/ 04 февраля 2015

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

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());
6 голосов
/ 25 ноября 2015

Вы можете использовать Humanizer пакет с Humanize Enums возможностью. Пример:

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

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

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)
5 голосов
/ 01 мая 2009

По отношению к Рэю Бойсену в коде есть ошибка: Перечисление 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();
...