Строковое представление Enum - PullRequest
875 голосов
/ 08 января 2009

У меня есть следующее перечисление:

public enum AuthenticationMethod
{
    FORMS = 1,
    WINDOWSAUTHENTICATION = 2,
    SINGLESIGNON = 3
}

Проблема, однако, в том, что мне нужно слово «FORMS», когда я спрашиваю AuthenticationMethod.FORMS, а не id 1.

Я нашел следующее решение этой проблемы ( ссылка ):

Сначала мне нужно создать собственный атрибут с именем "StringValue":

public class StringValue : System.Attribute
{
    private readonly string _value;

    public StringValue(string value)
    {
        _value = value;
    }

    public string Value
    {
        get { return _value; }
    }

}

Затем я могу добавить этот атрибут в мой перечислитель:

public enum AuthenticationMethod
{
    [StringValue("FORMS")]
    FORMS = 1,
    [StringValue("WINDOWS")]
    WINDOWSAUTHENTICATION = 2,
    [StringValue("SSO")]
    SINGLESIGNON = 3
}

И, конечно, мне нужно что-то, чтобы получить это StringValue:

public static class StringEnum
{
    public static string GetStringValue(Enum value)
    {
        string output = null;
        Type type = value.GetType();

        //Check first in our cached results...

        //Look for our 'StringValueAttribute' 

        //in the field's custom attributes

        FieldInfo fi = type.GetField(value.ToString());
        StringValue[] attrs =
           fi.GetCustomAttributes(typeof(StringValue),
                                   false) as StringValue[];
        if (attrs.Length > 0)
        {
            output = attrs[0].Value;
        }

        return output;
    }
}

Хорошо, теперь у меня есть инструменты для получения строкового значения для перечислителя. Затем я могу использовать его так:

string valueOfAuthenticationMethod = StringEnum.GetStringValue(AuthenticationMethod.FORMS);

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

Я также попробовал кое-что со словарем и статическими свойствами, но это было не лучше.

Ответы [ 35 ]

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

В своем вопросе вы никогда не говорили, что вам на самом деле нужно числовое значение перечисления.

Если вам не нужно и просто нужно перечисление типа string (которое не является целочисленным типом, поэтому не может быть основой перечисления), вот способ:

    static class AuthenticationMethod
    {
        public static readonly string
            FORMS = "Forms",
            WINDOWSAUTHENTICATION = "WindowsAuthentication";
    }

вы можете использовать тот же синтаксис, что и enum для ссылки на него

if (bla == AuthenticationMethod.FORMS)

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

10 голосов
/ 17 апреля 2012

Как я решил это как метод расширения:

using System.ComponentModel;
public static string GetDescription(this Enum value)
{
    var descriptionAttribute = (DescriptionAttribute)value.GetType()
        .GetField(value.ToString())
        .GetCustomAttributes(false)
        .Where(a => a is DescriptionAttribute)
        .FirstOrDefault();

    return descriptionAttribute != null ? descriptionAttribute.Description : value.ToString();
}

Enum:

public enum OrderType
{
    None = 0,
    [Description("New Card")]
    NewCard = 1,
    [Description("Reload")]
    Refill = 2
}

Использование (где o.OrderType - свойство с тем же именем, что и перечисление):

o.OrderType.GetDescription()

Что дает мне строку «Новая карта» или «Перезагрузить» вместо фактического значения перечисления NewCard и Refill.

10 голосов
/ 02 декабря 2011

Обновление: Посещение этой страницы, 8 лет спустя, после того, как долгое время не трогал C #, похоже, мой ответ больше не является лучшим решением. Мне очень нравится конвертер, связанный с атрибутами-функциями.

Если вы читаете это, убедитесь, что вы также ознакомились с другими ответами.
(подсказка: они выше этого)


Как и большинству из вас, мне действительно понравился выбранный ответ Якуба Штурца , но я также очень не хочу копировать-вставлять код и стараться делать это как можно меньше.

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

Основная проблема этого подхода основана на том факте, что, хотя значения Enum являются типобезопасными экземплярами, взаимодействие происходит со статической реализацией типа Enum Class. Так что с небольшой помощью магии дженериков, я думаю, я наконец-то получил правильный микс. Надеюсь, кто-то найдет это столь же полезным, как и я.

Начну с примера Якуба, но с использованием наследования и обобщений:

public sealed class AuthenticationMethod : EnumBase<AuthenticationMethod, int>
{
    public static readonly AuthenticationMethod FORMS =
        new AuthenticationMethod(1, "FORMS");
    public static readonly AuthenticationMethod WINDOWSAUTHENTICATION =
        new AuthenticationMethod(2, "WINDOWS");
    public static readonly AuthenticationMethod SINGLESIGNON =
        new AuthenticationMethod(3, "SSN");

    private AuthenticationMethod(int Value, String Name)
        : base( Value, Name ) { }
    public new static IEnumerable<AuthenticationMethod> All
    { get { return EnumBase<AuthenticationMethod, int>.All; } }
    public static explicit operator AuthenticationMethod(string str)
    { return Parse(str); }
}

А вот и базовый класс:

using System;
using System.Collections.Generic;
using System.Linq; // for the .AsEnumerable() method call

// E is the derived type-safe-enum class
// - this allows all static members to be truly unique to the specific
//   derived class
public class EnumBase<E, T> where E: EnumBase<E, T>
{
    #region Instance code
    public T Value { get; private set; }
    public string Name { get; private set; }

    protected EnumBase(T EnumValue, string Name)
    {
        Value = EnumValue;
        this.Name = Name;
        mapping.Add(Name, this);
    }

    public override string ToString() { return Name; }
    #endregion

    #region Static tools
    static private readonly Dictionary<string, EnumBase<E, T>> mapping;
    static EnumBase() { mapping = new Dictionary<string, EnumBase<E, T>>(); }
    protected static E Parse(string name)
    {
        EnumBase<E, T> result;
        if (mapping.TryGetValue(name, out result))
        {
            return (E)result;
        }

        throw new InvalidCastException();
    }
    // This is protected to force the child class to expose it's own static
    // method.
    // By recreating this static method at the derived class, static
    // initialization will be explicit, promising the mapping dictionary
    // will never be empty when this method is called.
    protected static IEnumerable<E> All
    { get { return mapping.Values.AsEnumerable().Cast<E>(); } }
    #endregion
}
9 голосов
/ 08 января 2009

Я согласен с Китом, но не могу голосовать (пока).

Я использую статический метод и оператор swith, чтобы вернуть именно то, что я хочу. В базе данных я храню tinyint, и мой код использует только фактическое перечисление, поэтому строки соответствуют требованиям пользовательского интерфейса. После многочисленных испытаний это привело к лучшей производительности и большей степени контроля над выходом.

public static string ToSimpleString(this enum)
{
     switch (enum)
     {
         case ComplexForms:
             return "ComplexForms";
             break;
     }
}

public static string ToFormattedString(this enum)
{
     switch (enum)
     {
         case ComplexForms:
             return "Complex Forms";
             break;
     }
}

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

9 голосов
/ 22 марта 2012

Если вы пришли сюда, чтобы реализовать простое «перечисление», но значения которого являются строками, а не целыми, вот самое простое решение:

    public sealed class MetricValueList
    {
        public static readonly string Brand = "A4082457-D467-E111-98DC-0026B9010912";
        public static readonly string Name = "B5B5E167-D467-E111-98DC-0026B9010912";
    }

Реализация:

var someStringVariable = MetricValueList.Brand;
6 голосов
/ 13 декабря 2010

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

[TypeConverter(typeof(CustomEnumTypeConverter(typeof(MyEnum))]
public enum MyEnum
{
  // The custom type converter will use the description attribute
  [Description("A custom description")]
  ValueWithCustomDescription,
  // This will be exposed exactly.
  Exact
}

должно быть

[TypeConverter(typeof(CustomEnumTypeConverter<MyEnum>))]
public enum MyEnum
{
  // The custom type converter will use the description attribute
  [Description("A custom description")]
  ValueWithCustomDescription,

  // This will be exposed exactly.
  Exact
}

Brillant!

6 голосов
/ 09 января 2009

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

  • Достаточно ли дружественны имена для моих значений enum или мне нужно предоставить более дружественные значения?
  • Нужно ли туда и обратно? То есть мне нужно будет взять текстовые значения и разобрать их в перечисляемые значения?
  • Это то, что мне нужно сделать для многих перечислений в моем проекте или только для одного?
  • Какие элементы пользовательского интерфейса я буду представлять эту информацию, в частности, буду ли я привязываться к пользовательскому интерфейсу или использовать листы свойств?
  • Это должно быть локализуемо?

Самый простой способ сделать это с помощью Enum.GetValue (и поддерживать циклическое отключение с помощью Enum.Parse). Также часто стоит создать TypeConverter, как предполагает Стив Митчам, для поддержки привязки пользовательского интерфейса. (Нет необходимости создавать TypeConverter при использовании листов свойств, что является одной из приятных особенностей листов свойств. Хотя лорд знает, что у них есть свои проблемы.)

В общем, если ответы на приведенные выше вопросы показывают, что это не сработает, мой следующий шаг - создать и заполнить статический Dictionary<MyEnum, string> или, возможно, Dictionary<Type, Dictionary<int, string>>. Я склонен пропустить промежуточный шаг decorate-the-code-with-attribute, потому что следующим шагом обычно является необходимость изменения дружественных значений после развертывания (часто, но не всегда, из-за локализации).

4 голосов
/ 01 февраля 2012

Мой вариант

public struct Colors
{
    private String current;

    private static string red = "#ff0000";
    private static string green = "#00ff00";
    private static string blue = "#0000ff";

    private static IList<String> possibleColors; 

    public static Colors Red { get { return (Colors) red; } }
    public static Colors Green { get { return (Colors) green; } }
    public static Colors Blue { get { return (Colors) blue; } }

    static Colors()
    {
        possibleColors = new List<string>() {red, green, blue};
    }

    public static explicit operator String(Colors value)
    {
        return value.current;
    }

    public static explicit operator Colors(String value)
    {
        if (!possibleColors.Contains(value))
        {
            throw new InvalidCastException();
        }

        Colors color = new Colors();
        color.current = value;
        return color;
    }

    public static bool operator ==(Colors left, Colors right)
    {
        return left.current == right.current;
    }

    public static bool operator !=(Colors left, Colors right)
    {
        return left.current != right.current;
    }

    public bool Equals(Colors other)
    {
        return Equals(other.current, current);
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (obj.GetType() != typeof(Colors)) return false;
        return Equals((Colors)obj);
    }

    public override int GetHashCode()
    {
        return (current != null ? current.GetHashCode() : 0);
    }

    public override string ToString()
    {
        return current;
    }
}

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

Colors color1 = Colors.Red;
Console.WriteLine(color1); // #ff0000

Colors color2 = (Colors) "#00ff00";
Console.WriteLine(color2); // #00ff00

// Colors color3 = "#0000ff"; // Compilation error
// String color4 = Colors.Red; // Compilation error

Colors color5 = (Colors)"#ff0000";
Console.WriteLine(color1 == color5); // True

Colors color6 = (Colors)"#00ff00";
Console.WriteLine(color1 == color6); // False

Кроме того, я думаю, что если потребуется много таких перечислений, можно использовать генерацию кода (например, T4).

3 голосов
/ 20 ноября 2009

Если вы думаете о проблеме, которую мы пытаемся решить, это не перечисление, которое нам нужно вообще. Нам нужен объект, который позволяет определенному количеству значений быть связанным друг с другом; другими словами, чтобы определить класс.

Типичный для enum шаблон Jakub Šturc - лучший вариант, который я здесь вижу.

Посмотрите на это:

  • Он имеет закрытый конструктор, поэтому только сам класс может определять допустимые значения.
  • Это закрытый класс, поэтому значения нельзя изменить с помощью наследования.
  • Это безопасный тип, позволяющий вашим методам требовать только этот тип.
  • Нет доступа к производительности отражения, полученной при доступе к значениям.
  • И, наконец, его можно изменить, чтобы связать более двух полей вместе, например, Имя, Описание и Числовое значение.
3 голосов
/ 06 февраля 2013

для меня прагматический подход - класс внутри класса, пример:

public class MSEModel
{
    class WITS
    {
        public const string DATE = "5005";
        public const string TIME = "5006";
        public const string MD = "5008";
        public const string ROP = "5075";
        public const string WOB = "5073";
        public const string RPM = "7001";
... 
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...