Строковое представление Enum

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

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

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

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

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

BenAlabaster 08.01.2009 19:15

Это «Перечисление», а не «Перечислитель».

Ed S. 09.01.2009 05:27

MSDN рекомендует классы атрибутов суффикса с суффиксом «Атрибут». Итак "класс StringValueAttribute";)

serhio 30.06.2011 18:53

Я согласен с @BenAlabaster, на самом деле это довольно гибко. Кроме того, вы можете сделать это методом расширения, просто добавив this перед Enum в вашем статическом методе. Тогда можно делать AuthenticationMethod.Forms.GetStringValue();

Justin Pihony 10.05.2012 22:56

Этот подход использует отражение для чтения значений атрибутов, и, по моему опыту, он очень медленный, если вам приходится вызывать GetStringValue () много раз. Шаблон type-safe-enum работает быстрее.

Rn222 04.03.2014 02:04

Если вы все же идете по маршруту атрибутов, вместо того, чтобы прокручивать свой собственный, вы можете использовать System.ComponentModel.DescriptionAttribute.

Phil Cooper 23.07.2014 12:23

Почему вы испытываете все эти проблемы, если вы можете просто вызвать .ToString() в перечислении, чтобы получить строку «FORMS»?

Nick 30.04.2015 23:30
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
932
7
786 119
37
Перейти к ответу Данный вопрос помечен как решенный

Ответы 37

Метод использования

Enum.GetName(Type MyEnumType,  object enumvariable)  

как в (Предположим, что Shipper - это определенное Enum)

Shipper x = Shipper.FederalExpress;
string s = Enum.GetName(typeof(Shipper), x);

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

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

lc. 08.01.2009 17:21

Enum.GetName отражает имена полей в перечислении - так же, как .ToString (). Если производительность является проблемой, это может быть проблемой. Я бы не стал беспокоиться об этом, если вы не конвертируете множество перечислений.

Keith 08.01.2009 17:26

Другой вариант, который следует рассмотреть, если вам нужно перечисление с дополнительной функциональностью, - это «свернуть свой собственный» с помощью структуры ... вы добавляете статические свойства с именами только для чтения для представления значений перечисления, которые инициализируются для конструкторов, которые генерируют отдельные экземпляры структуры ...

Charles Bretana 08.01.2009 17:39

затем вы можете добавить любые другие члены структуры, которые захотите, чтобы реализовать любую функциональность, которую вы хотите, чтобы это "перечисление" имело ...

Charles Bretana 08.01.2009 17:40

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

Joel Coehoorn 08.01.2009 17:49

@Charles: Ага, я сделал это для перечисляемых значений, которые требуют более сложной логики синтаксического анализа.

Ed S. 04.02.2011 01:18

@Ed, да, это чрезвычайно полезный метод, с которым не многие знакомы

Charles Bretana 04.02.2011 03:26

почему не Shipper.FederalExpress.ToString(). Я пробовал в C# 4.0, и результат был таким же, как Enum.GetName

Tyler Long 05.08.2012 18:33

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

Charles Bretana 02.10.2012 06:08

@tyler, и, поскольку он работает только во время компиляции, если вы знаете, какой член вам нужен, в таком случае почему бы вместо Shipper.FederalExpress.ToString() просто не поставить "FederalExpress"?

Charles Bretana 03.03.2013 19:49

@CharlesBretana, я не понимаю твоей точки зрения. Мне не обязательно знать, кого я ищу. Предположим, что это переменная с именем x (точно такая же, как в вашем ответе), разве x.toString() не работает? Я не пробовал, потому что прошло много времени с тех пор, как я написал последнюю строчку C#. если это не сработает, тогда я понимаю вашу точку зрения.

Tyler Long 06.03.2013 06:44

@tyler, мне нужно сделать это сейчас, чтобы быть уверенным, но, насколько я помню, если вы не переопределите это (что вы не можете сделать для перечисления), ToString() для переменной просто возвращает имя типа. Это одна из причин, по которой вы по привычке переопределяете ToString() для других объявленных типов, чтобы отладчик отображал что-то полезное (а не только имя типа) при просмотре содержимого коллекций и тому подобного.

Charles Bretana 07.03.2013 19:39

К сожалению, отражение для получения атрибутов в перечислениях происходит довольно медленно:

См. Этот вопрос: Кто-нибудь знает, как быстро перейти к настраиваемым атрибутам значения перечисления?

.ToString() также довольно медленно работает с перечислениями.

Однако вы можете написать методы расширения для перечислений:

public static string GetName( this MyEnum input ) {
    switch ( input ) {
        case MyEnum.WINDOWSAUTHENTICATION:
            return "Windows";
        //and so on
    }
}

Это не очень хорошо, но будет быстро и не потребует отражения атрибутов или имени поля.


Обновление C# 6

Если вы можете использовать C# 6, тогда новый оператор nameof работает для перечислений, поэтому nameof(MyEnum.WINDOWSAUTHENTICATION) будет преобразован в "WINDOWSAUTHENTICATION" в время компиляции, что сделает его самым быстрым способом получения имен перечислений.

Обратите внимание, что это преобразует явное перечисление во встроенную константу, поэтому это не работает для перечислений, которые у вас есть в переменной. Так:

nameof(AuthenticationMethod.FORMS) == "FORMS"

Но...

var myMethod = AuthenticationMethod.FORMS;
nameof(myMethod) == "myMethod"

Вы можете получить значения атрибутов один раз и поместить их в Dictionary <MyEnum, string>, чтобы сохранить декларативный аспект.

Jon Skeet 08.01.2009 17:38

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

Keith 08.01.2009 19:04

Спасибо, Джон и Кейт, в итоге я воспользовался вашим предложением из Словаря. Отлично работает (и быстро!).

Helge Klein 24.11.2010 01:22

@JonSkeet Я знаю, что это старое. Но как этого добиться?

user919426 12.02.2015 16:22

@ user919426: Достичь хочешь? Помещать их в словарь? Просто создайте словарь, в идеале с инициализатором коллекции ... непонятно, о чем вы просите.

Jon Skeet 12.02.2015 16:52

Спасибо за ответ @JonSkeet. Извините, я хотел спросить о фактической реализации. Как "получить один раз" без повторного звонка. Воспользуемся вашим ответом, чтобы понять логику.

user919426 12.02.2015 19:45
nameof() работает, только если вы заранее знаете экземпляр enum. т.е. в var enumVal = MyEnum.Value; значение, возвращаемое nameof(enumVal), будет «enumVal», а не «Value». Это функция компилятора, а не среда выполнения.
JoeBrockhaus 01.03.2016 21:17

@JoeBrockhaus Да, хотя здесь нет экземпляра - nameof(MyEnum.Value) станет "Value" во время компиляции, экземпляра MyEnum нет.

Keith 02.03.2016 11:13

@Keith Не уверен, что я понимаю - этот вопрос (и ответ) касается получения имени экземпляра перечисления. Кто-то может легко прийти сюда, прочитать этот ответ, увидеть упоминание о том, что nameof () является «самым быстрым способом получения имен перечислений», и использовать его, думая, что они получат желаемые результаты. Я знаю, что ваш пример не показывает этот экземпляр, но, возможно, стоит отредактировать ответ, чтобы уточнить.

JoeBrockhaus 02.03.2016 17:36

@JoeBrockhaus, похоже, все примеры в вопросе получают имя явного перечисления, например (из вопроса) StringEnum.GetStringValue(AuthenticationMethod.FORMS) - в этом контексте nameof(AuthenticationMethod.FORMS) быстрее. Я совершенно четко ответил, что это строго оптимизация времени компиляции, но я добавлю пояснение.

Keith 02.03.2016 17:40

Вы можете ссылаться на имя, а не на значение, используя ToString ()

Console.WriteLine("Auth method: {0}", AuthenticationMethod.Forms.ToString());

Документация здесь:

http://msdn.microsoft.com/en-us/library/16c1xs4z.aspx

... и если вы назовете свои перечисления в Pascal Case (как я, например, ThisIsMyEnumValue = 1 и т. д.), вы можете использовать очень простое регулярное выражение для печати удобной формы:

static string ToFriendlyCase(this string EnumString)
{
    return Regex.Replace(EnumString, "(?!^)([A-Z])", " $1");
}

который легко вызывается из любой строки:

Console.WriteLine("ConvertMyCrazyPascalCaseSentenceToFriendlyCase".ToFriendlyCase());

Выходы:

Convert My Crazy Pascal Case Sentence To Friendly Case

Это избавляет от необходимости бегать по домам, создавая настраиваемые атрибуты и прикрепляя их к вашим перечислениям, или используя таблицы поиска, чтобы объединить значение перечисления с дружественной строкой, и, что лучше всего, оно самоуправляемое и может использоваться в любой строке Pascal Case, которая бесконечно более многоразовый. Конечно, это не позволяет вам иметь понятное имя разные, чем ваше перечисление, которое предоставляет ваше решение.

Хотя мне нравится ваше оригинальное решение для более сложных сценариев. Вы могли бы продвинуть свое решение еще на один шаг и сделать GetStringValue методом расширения вашего перечисления, и тогда вам не нужно было бы ссылаться на него, как StringEnum.GetStringValue ...

public static string GetStringValue(this AuthenticationMethod value)
{
  string output = null;
  Type type = value.GetType();
  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;
}

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

Console.WriteLine(AuthenticationMethod.SSO.GetStringValue());

Это не поможет, если для «понятного имени» нужен пробел. Например, "Проверка подлинности с помощью форм"

Ray Booysen 08.01.2009 17:26

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

BenAlabaster 08.01.2009 18:10

Автоматический интервал между именами Pascal Case становится проблематичным, если они содержат сокращения, которые должны быть написаны с заглавной буквы, например XML или GPS.

Richard Ev 20.07.2012 17:47

@RichardEv, для этого не существует идеального регулярного выражения, но вот одно, которое должно работать немного лучше с сокращениями. "(?!^)([^A-Z])([A-Z])", "$1 $2". Итак, HereIsATEST становится Here Is ATEST.

sparebytes 13.08.2013 03:51

Не изящно делать эти маленькие "хаки", каковы они есть. Я понимаю, что говорит OP, и пытаюсь найти аналогичное решение, то есть используя элегантность Enums, но имея возможность легко получить доступ к связанному сообщению. Единственное решение, которое я могу придумать, - это применить какое-то сопоставление между именем перечисления и строковым значением, но это не решает проблему сохранения строковых данных (однако делает его практичным для сценариев, где вам нужно иметь несколько регионов и т. д. )

Tahir Khalid 01.04.2017 22:35
Ответ принят как подходящий

Попробуйте шаблон безопасное перечисление типов.

public sealed class AuthenticationMethod {

    private readonly String name;
    private readonly int value;

    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){
        this.name = name;
        this.value = value;
    }

    public override String ToString(){
        return name;
    }

}

Обновлять Явное (или неявное) преобразование типа может быть выполнено с помощью

  • добавление статического поля с отображением

    private static readonly Dictionary<string, AuthenticationMethod> instance = new Dictionary<string,AuthenticationMethod>();
    
    • n.b. Чтобы инициализация полей «enum member» не вызывала исключение NullReferenceException при вызове конструктора экземпляра, обязательно поместите поле Dictionary перед полями «enum member» в вашем классе. Это связано с тем, что инициализаторы статических полей вызываются в порядке объявления и перед статическим конструктором, создавая странную и необходимую, но сбивающую с толку ситуацию, когда конструктор экземпляра может быть вызван до инициализации всех статических полей и до вызова статического конструктора.
  • заполнение этого сопоставления в конструкторе экземпляра

    instance[name] = this;
    
  • и добавив оператор преобразования пользовательского типа

    public static explicit operator AuthenticationMethod(string str)
    {
        AuthenticationMethod result;
        if (instance.TryGetValue(str, out result))
            return result;
        else
            throw new InvalidCastException();
    }
    

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

Ant 25.02.2010 12:18

@Ant: Мне не нужно. Поскольку у нас есть только один экземпляр каждого AuthenticationMethod, ссылочное равенство, унаследованное от Object, работает нормально.

Jakub Šturc 26.02.2010 12:24

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

InvisibleBacon 23.04.2010 23:25

@InvisibleBacon: Верно, переключатель в этом случае не вариант. Однако иногда вы можете заменить switch полиморфизмом. См .: sourcemaking.com/refactoring/…

Jakub Šturc 22.06.2010 13:17

Это странно и кажется мне опасным ... Что мешает этому: AuthenticationMethod a = new AuthenticationMethod (); var b = a.FORMS.SINGLESIGNON; поскольку a.FORMS сам по себе является экземпляром AuthenticationMethod?

tyriker 13.10.2010 23:16

@tyriker: Компилятор умеет. Конструктор является частным, поэтому вы не можете создать новый экземпляр. Также статические члены недоступны через экземпляр.

Jakub Šturc 14.10.2010 10:56

@Jakub Очень интересно. Мне пришлось поиграть с ним, чтобы понять, как его использовать и понять его преимущества. Это общедоступный нестатический класс, но его нельзя создать, и вы можете получить доступ только к его статическим членам. По сути, он ведет себя как перечисление. Но лучшая часть ... статические члены типизированы для класса, а не для общей строки или int. Это ... [подождите] ... безопасное перечисление! Спасибо, что помогли мне понять.

tyriker 14.10.2010 21:56

Я вижу проблему, когда вы хотите смешать это с внешними значениями (например, DB). Вы хотите преобразовать это в строго типизированный, но не можете. Также невозможно написать цикл, который сопоставляет строку с правильным строго типизированным значением. Все, что вы можете сделать, это серия уродливых утверждений if else.

Sedat Kapanoglu 19.01.2011 15:14

@ssg: Извините, я не понимаю вашей точки зрения. Вы можете реализовать явные / неявные операторы в классе AuthenticationMethod, если они вам действительно нужны.

Jakub Šturc 19.01.2011 16:14

@Jakub: Допустим, у вас 20 имен, и вы используете те же имена в БД. Вы читаете запись и хотите преобразовать ее в enum в коде. Вам нужно сопоставить каждую строку со строго типизированным эквивалентом, проверив серию ifs. Как здесь помогает перегрузка оператора?

Sedat Kapanoglu 19.01.2011 17:57

@ssg: Я обновил образец. Итак, теперь, если вы можете написать var strongTypeInstance = (AuthenticationMethod) "FORMS"; Я не уверен насчет операторов преобразования. Возможно, статический метод Parse сработал бы лучше. Если хочешь, можешь получить.

Jakub Šturc 19.01.2011 21:24

Для получения дополнительной информации о том, как реализовать этот шаблон, взгляните на javacamp.org/designPattern/enum.html. Я знаю, что это не реализация C#, но все же понятно.

pb. 14.10.2011 14:14

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

Chris Moschini 16.02.2012 01:09

Эээ ... Это просто вырожденное непересекающееся объединение, или размеченное объединение, или помеченное объединение en.wikipedia.org/wiki/Tagged_union. Это было с 1960-х, ребята. Не нужно придумывать яркое название для чего-то, что существует уже несколько десятилетий и уже имеет идеально подходящее название (пришедшее из математики).

Mauricio Scheffer 29.05.2012 18:05

@MauricioScheffer: Я могу спорить о вырождении, но вы правы. Все это было раньше, и все это повторится снова.

Jakub Šturc 29.05.2012 22:44

одно изменение, которое я бы внес в это, - хранить строковые значения не как поля экземпляра, а как статические константы. Зачем тратить хранилище? Просто поместите оператор switch case в ToString (); тогда строковые значения сохраняются только в определении типа, а не в каждом экземпляре «перечисления». (тип). Вы даже можете просто жестко запрограммировать их в операторах case переключателя. Кроме того, это яркий пример ситуации, когда вы должны использовать структуру вместо класса. Это (или должно быть) в основном неизменяемое.

Charles Bretana 11.06.2012 16:19

@CharlesBretana: Вы говорите о переменной private static readonly Dictionary<string, AuthenticationMethod> instance? И, пожалуйста, подробнее о структуре. Я не уверен, что ты сможешь сделать это таким гениальным ...

Robert Koritnik 19.06.2012 15:36

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

anaximander 22.02.2013 19:00

Те, кто обеспокоен тем, что сбивают с толку более поздних кодировщиков ... Вы можете использовать слово «Enum» в имени класса, чтобы побудить программистов-пользователей просмотреть исходный код, если он доступен.

Derrick 22.03.2013 23:01

Это хороший шаблон, но у меня проблемы с сериализацией ... Чтобы реализовать ISerializable, ваш класс должен включать специальный конструктор, который нарушит запечатанный / одноэлементный характер кода. Я также пытаюсь использовать этот TypeSafeEnum в свойстве UserControl - например, этот <uc:MyUC AuthMethod = "FORMS" /> - поэтому мне пришлось реализовать TypeConverter, и это, похоже, вызывает множество других проблем, одна из которых заключается в том, что статический конструктор не запускается до того, как TypeConverter попытается выполнить преобразования. (Обходной путь: просто сохраните опору как строку и примените при необходимости)

sparebytes 13.08.2013 20:17

Поскольку значения type-safe-enum не являются константами времени компиляции (как обычные значения enum и строки), вы не можете использовать их везде, где требуются значения констант времени компиляции. В моем случае это были атрибуты, но я представляю еще кое-что. switch, однако, не проблема, поскольку вы можете реализовать такие вещи в классе type-safe-enum, таким образом сделав его умным перечислением.

mbx 28.02.2014 18:44

@ JakubŠturc На случай, если вы ответите на это: Как мне перечислить это как enum Enum.GetValues()? Единственное, о чем я могу думать, - это отражение в статической функции Enumerate().

MikeH 23.10.2014 02:13

@ JakubŠturc ссылка javacamp.org/designPattern/enum.html больше не работает.

Amadeus Sánchez 01.12.2015 18:02

Мне нравится этот шаблон, и я использовал его раньше во многих формах, но, возможно, одна вещь, которая возвращает меня к Перечисления, - это то, что я не могу использую этот TypeSafeEnum как значение по умолчанию для параметра, поскольку это не константа времени компиляции. Есть идеи, как обойти эту икоту?

SkyFighter 13.04.2016 20:03

Работает до тех пор, пока значение не нужно сериализовать.

Rune Aamodt 23.08.2016 19:12

@SkyFighter, если вы посмотрите на версию deadlydog, которая вместо этого делает int общедоступным перечислением, вы можете использовать это для значений по умолчанию или в операторе switch.

Jason Beck 12.01.2018 00:47

Изначально я использовал вашу реализацию, но подумал, что мне неприятно писать так много кода вручную, поэтому я создал несколько шаблонов T4, чтобы помочь с этим. stackoverflow.com/a/52209481/1582837 Он предоставляет способы перебора значений с помощью Enum.Values. Может также помочь со значениями по умолчанию для параметров. @MikeH

Pangamma 06.09.2018 20:53

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

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

        }

В качестве примера:

public enum Cycle : int
{        
   [Description("Daily Cycle")]
   Daily = 1,
   Weekly,
   Monthly
}

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

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

Я использую статический метод и оператор 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;
     }
}

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

Опция 1:

public sealed class FormsAuth
{
     public override string ToString{return "Forms Authtentication";}
}
public sealed class WindowsAuth
{
     public override string ToString{return "Windows Authtentication";}
}

public sealed class SsoAuth
{
     public override string ToString{return "SSO";}
}

а потом

object auth = new SsoAuth(); //or whatever

//...
//...
// blablabla

DoSomethingWithTheAuth(auth.ToString());

Вариант 2:

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

public class MyClass
{
    private Dictionary<AuthenticationMethod, String> map = new Dictionary<AuthenticationMethod, String>();
    public MyClass()
    {
         map.Add(AuthenticationMethod.FORMS,"Forms Authentication");
         map.Add(AuthenticationMethod.WINDOWSAUTHENTICATION ,"Windows Authentication");
         map.Add(AuthenticationMethod.SINGLESIGNON ,"SSo Authentication");
    }
}

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

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

Базовое использование будет выглядеть так

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

Код для преобразователя настраиваемого типа перечисления следующий:

public class CustomEnumTypeConverter<T> : EnumConverter
    where T : struct
{
    private static readonly Dictionary<T,string> s_toString = 
      new Dictionary<T, string>();

    private static readonly Dictionary<string, T> s_toValue = 
      new Dictionary<string, T>();

    private static bool s_isInitialized;

    static CustomEnumTypeConverter()
    {
        System.Diagnostics.Debug.Assert(typeof(T).IsEnum,
          "The custom enum class must be used with an enum type.");
    }

    public CustomEnumTypeConverter() : base(typeof(T))
    {
        if (!s_isInitialized)
        {
            Initialize();
            s_isInitialized = true;
        }
    }

    protected void Initialize()
    {
        foreach (T item in Enum.GetValues(typeof(T)))
        {
            string description = GetDescription(item);
            s_toString[item] = description;
            s_toValue[description] = item;
        }
    }

    private static string GetDescription(T optionValue)
    {
        var optionDescription = optionValue.ToString();
        var optionInfo = typeof(T).GetField(optionDescription);
        if (Attribute.IsDefined(optionInfo, typeof(DescriptionAttribute)))
        {
            var attribute = 
              (DescriptionAttribute)Attribute.
                 GetCustomAttribute(optionInfo, typeof(DescriptionAttribute));
            return attribute.Description;
        }
        return optionDescription;
    }

    public override object ConvertTo(ITypeDescriptorContext context, 
       System.Globalization.CultureInfo culture, 
       object value, Type destinationType)
    {
        var optionValue = (T)value;

        if (destinationType == typeof(string) && 
            s_toString.ContainsKey(optionValue))
        {
            return s_toString[optionValue];
        }

        return base.ConvertTo(context, culture, value, destinationType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, 
       System.Globalization.CultureInfo culture, object value)
    {
        var stringValue = value as string;

        if (!string.IsNullOrEmpty(stringValue) && s_toValue.ContainsKey(stringValue))
        {
            return s_toValue[stringValue];
        }

        return base.ConvertFrom(context, culture, value);
    }
}

}

Как это использовать? благодарить. пример: MyEnum.ValueWithCustomDescription.??() или что-то в этом роде?

Trương Quốc Khánh 31.08.2020 10:02

Этот ответ был написан около десяти лет назад, поэтому я действительно не уверен в исходном контексте. Проблема с SO в том, что он существует вечно. Я считаю, что это было связано с добавлением данных, которые должны отображаться в пользовательских интерфейсах, таких как WinForms или WPF, и в этом случае простая привязка свойства или коллекции к элементу управления пользовательского интерфейса напрямую подберет преобразователь типов.

Steve Mitcham 01.09.2020 19:02

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

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

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

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

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

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

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

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

на основе MSDN: http://msdn.microsoft.com/en-us/library/cc138362.aspx

foreach (string str in Enum.GetNames(typeof(enumHeaderField)))
{
    Debug.WriteLine(str);
}

str будет именами полей

это даст имя перечисления, вы также можете использовать для этого ToString (), это не то, о чем просили. Checkout msdn.microsoft.com/en-us/library/system.enum.getname.aspx для получения дополнительной информации о вашем бубу

Mickey Perlstein 07.05.2012 18:52

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

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

Блестяще!

Использовать объект Enum.Parse (System.Type enumType, строковое значение, bool ignoreCase); получил от http://blogs.msdn.com/b/tim/archive/2004/04/02/106310.aspx

Вам следует пересмотреть этот ответ, включив в него пример, а не просто ссылку.

Bernie White 21.04.2012 14:04

и прочтите вопрос, тот же ответ, что и выше msdn.microsoft.com/en-us/library/system.enum.getname.aspx

Mickey Perlstein 07.05.2012 18:53

Подход, который я нашел для интернационализации перечислений или получения текста перечислений из соответствующих файлов ресурсов, заключается в создании класса атрибута путем наследования класса DescriptionAttribute.

public class EnumResourceAttribute : DescriptionAttribute
{

    public Type ResourceType { get; private set; }
    public string ResourceName { get; private set; }
    public int SortOrder { get; private set; }
    public EnumResourceAttribute(Type ResourceType,
                         string ResourceName,
                         int SortOrder)
    {

        this.ResourceType = ResourceType;
        this.ResourceName = ResourceName;
        this.SortOrder = SortOrder;
    }
}

Создайте еще один статический класс, который будет предоставлять методы расширения для GetString и GetStrings.

public static class EnumHelper
{
    public static string GetString(this Enum value)
    {
        EnumResourceAttribute ea =
       (EnumResourceAttribute)value.GetType().GetField(value.ToString())
        .GetCustomAttributes(typeof(EnumResourceAttribute), false)
         .FirstOrDefault();
        if (ea != null)
        {
            PropertyInfo pi = ea.ResourceType
             .GetProperty(CommonConstants.ResourceManager);
            if (pi != null)
            {
                ResourceManager rm = (ResourceManager)pi
                .GetValue(null, null);
                return rm.GetString(ea.ResourceName);
            }

        }
        return string.Empty;
    }


    public static IList GetStrings(this Type enumType)
    {
        List<string> stringList = new List<string>();
        FieldInfo[] fiArray = enumType.GetFields();
        foreach (FieldInfo fi in fiArray)
        {
            EnumResourceAttribute ea =
                (EnumResourceAttribute)fi
                     .GetCustomAttributes(typeof(EnumResourceAttribute), false)
                     .FirstOrDefault();
            if (ea != null)
            {
                PropertyInfo pi = ea.ResourceType
                                    .GetProperty(CommonConstants.ResourceManager);
                if (pi != null)
                {
                    ResourceManager rm = (ResourceManager)pi
                                          .GetValue(null, null);
                    stringList.Add(rm.GetString(ea.ResourceName));
                }
            }
        }
        return stringList.ToList();
    }
}

И на элементах вашего Enum вы можете написать:

public enum Priority
{
     [EnumResourceAttribute(typeof(Resources.AdviceModule), Resources.ResourceNames.AdviceCreateAdviceExternaPriorityMemberHigh, 1)]
    High,
     [EnumResourceAttribute(typeof(Resources.AdviceModule), Resources.ResourceNames.AdviceCreateAdviceExternaPriorityMemberRoutine, 2)]
    Routine
}

Где Resources.ResourceNames.AdviceCreateAdviceExternaPriorityMemberHigh & Resources.ResourceNames.AdviceCreateAdviceExternaPriorityMemberRoutine - это константы в файле ресурсов, или вы можете указать строки, значения которых могут быть доступны в разных культурах.

Если вы реализуете свое веб-приложение в архитектуре MVC, создайте свойство

private IList result;
public IList Result
{
    get
    {
        result = typeof(Priority).GetStrings();
        return result;
    }
}

и в вашем файле .cshtml вы можете просто привязать перечисление к своему выпадающему списку, например:

@Html.DropDownListFor(model => Model.vwClinicalInfo.Priority, new SelectList(Model.Result))

Спасибо Ратнеш

Обновлять: Посещение этой страницы 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
}

Вы можете вызвать производный статический конструктор из базового статического конструктора. Я все еще изучаю его, но пока не обнаружил никаких проблем: stackoverflow.com/questions/55290034/…

Cory-G 22.03.2019 02:34

Мой вариант

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

Я использую метод расширения:

public static class AttributesHelperExtension
    {
        public static string ToDescription(this Enum value)
        {
            var da = (DescriptionAttribute[])(value.GetType().GetField(value.ToString())).GetCustomAttributes(typeof(DescriptionAttribute), false);
            return da.Length > 0 ? da[0].Description : value.ToString();
        }
}

Теперь украсьте enum:

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

Когда ты звонишь

AuthenticationMethod.FORMS.ToDescription() вы получите "FORMS".

Мне пришлось добавить using System.ComponentModel;. Кроме того, этот метод работает только в том случае, если вы хотите, чтобы значение String совпадало с именем Enum. OP хотел другое значение.

elcool 23.01.2013 19:02

Разве вы не имеете в виду, когда звоните в AuthenticationMethod.FORMS.ToDescription()?

nicodemus13 01.02.2014 23:56

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

    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;

Вероятно, лучше сделать переменные постоянными вместо использования static readonly.

AndrewL 22.04.2013 21:35

consts не подходят для общедоступных классов, поскольку они запекаются во время компиляции, вы не можете заменить стороннюю DLL без перекомпиляции всего кода с помощью const. Смещение производительности consts vs static readonly незначительно.

Kristian Williams 24.08.2017 13:46

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

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

Это дает мне строку «New Card» или «Reload» вместо фактического значения перечисления NewCard и Refill.

Для полноты вы должны включить копию своего класса DescriptionAttribute.

Bernie White 21.04.2012 14:03

Берни, DescriptionAttribute находится в System.ComponentModel

agentnega 08.05.2012 20:00

Мой ответ, работая над ответом @ user29964 (который, безусловно, является самым простым и близким к Enum), является

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

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

        public string Value
        {
            get { return _value; }
        }



        public static string GetStringValue(Enum Flagvalue)
        {
            Type type = Flagvalue.GetType();
            string[] flags = Flagvalue.ToString().Split(',').Select(x => x.Trim()).ToArray();
            List<string> values = new List<string>();

            for (int i = 0; i < flags.Length; i++)
            {

                FieldInfo fi = type.GetField(flags[i].ToString());

                StringValue[] attrs =
                   fi.GetCustomAttributes(typeof(StringValue),
                                           false) as StringValue[];
                if (attrs.Length > 0)
                {
                    values.Add(attrs[0].Value);
                }
            }
            return String.Join(",", values);

        }

Применение

[Flags]
    public enum CompeteMetric
    {

        /// <summary>
        /// u
        /// </summary>
        [StringValue("u")]//Json mapping
        Basic_UniqueVisitors = 1 //Basic
             ,
        /// <summary>
        /// vi
        /// </summary>
        [StringValue("vi")]//json mapping
        Basic_Visits = 2// Basic
            ,
        /// <summary>
        /// rank
        /// </summary>
        [StringValue("rank")]//json mapping
        Basic_Rank = 4//Basic
 }

Пример

        CompeteMetric metrics = CompeteMetric.Basic_Visits | CompeteMetric.Basic_Rank;
        string strmetrics = StringValue.GetStringValue(metrics);

это вернется "vi, ранг"

Просто используйте метод ToString()

public enum any{Tomato=0,Melon,Watermelon}

Чтобы сослаться на строку Tomato, просто используйте

any.Tomato.ToString();

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

Rafe 23.09.2012 10:52

@Brent Потому что чаще всего у вас значение .ToString() отличается от необходимого вам удобного значения.

Novitchi S 23.04.2014 17:35

@Brent - потому что это отличается от задаваемого вопроса. Возникает вопрос, как получить эту строку из переменной, которой присвоено перечислимое значение. Это динамично во время выполнения. Это проверка определения типа и установки во время выполнения.

Hogan 10.07.2015 22:03

@Hogan - ToString () также работает с переменными: any fruit = any.Tomato;string tomato = fruit.ToString();

LiborV 14.07.2017 17:49

@LiborV - пожалуйста, помните, что это было написано в 09 - C# тогда был другим, и ToString () в экземпляре перечисления сделал что-то другое.

Hogan 14.07.2017 23:16

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

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

Что ж, после прочтения всего вышесказанного я чувствую, что ребята слишком усложнили задачу преобразования счетчиков в строки. Мне понравилась идея наличия атрибутов над перечисляемыми полями, но я думаю, что атрибуты в основном используются для метаданных, но в вашем случае я думаю, что все, что вам нужно, - это своего рода локализация.

public enum Color 
{ Red = 1, Green = 2, Blue = 3}


public static EnumUtils 
{
   public static string GetEnumResourceString(object enumValue)
    {
        Type enumType = enumValue.GetType();
        string value = Enum.GetName(enumValue.GetType(), enumValue);
        string resourceKey = String.Format("{0}_{1}", enumType.Name, value);
        string result = Resources.Enums.ResourceManager.GetString(resourceKey);
        if (string.IsNullOrEmpty(result))
        {
            result = String.Format("{0}", value);
        }
        return result;
    }
}

Теперь, если мы попытаемся вызвать вышеуказанный метод, мы можем назвать его так

public void Foo()
{
  var col = Color.Red;
  Console.WriteLine (EnumUtils.GetEnumResourceString (col));
}

Все, что вам нужно сделать, это просто создать файл ресурсов, содержащий все значения перечислителя и соответствующие строки.

Resource Name          Resource Value
Color_Red              My String Color in Red
Color_Blue             Blueeey
Color_Green            Hulk Color

Что на самом деле очень хорошо в этом, так это то, что это будет очень полезно, если вам нужно локализовать ваше приложение, поскольку все, что вам нужно сделать, это просто создать еще один файл ресурсов с вашим новым языком! и Во-ла!

Вот еще один способ связать строки с перечислениями:

struct DATABASE {
    public enum enums {NOTCONNECTED, CONNECTED, ERROR}
    static List<string> strings =
        new List<string>() {"Not Connected", "Connected", "Error"};

    public string GetString(DATABASE.enums value) {
        return strings[(int)value];
    }
}

Этот метод называется так:

public FormMain() {
    DATABASE dbEnum;

    string enumName = dbEnum.GetString(DATABASE.enums.NOTCONNECTED);
}

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

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

Мне очень нравится ответ Якуба Штурца, но его недостатком является то, что вы не можете использовать его с оператором switch-case. Вот немного измененная версия его ответа, которую можно использовать с оператором switch:

public sealed class AuthenticationMethod
{
    #region This code never needs to change.
    private readonly string _name;
    public readonly Values Value;

    private AuthenticationMethod(Values value, String name){
        this._name = name;
        this.Value = value;
    }

    public override String ToString(){
        return _name;
    }
    #endregion

    public enum Values
    {
        Forms = 1,
        Windows = 2,
        SSN = 3
    }

    public static readonly AuthenticationMethod FORMS = new AuthenticationMethod (Values.Forms, "FORMS");
    public static readonly AuthenticationMethod WINDOWSAUTHENTICATION = new AuthenticationMethod (Values.Windows, "WINDOWS");
    public static readonly AuthenticationMethod SINGLESIGNON = new AuthenticationMethod (Values.SSN, "SSN");
}

Таким образом, вы получаете все преимущества ответа Якуба Штурца, плюс мы можем использовать его с таким оператором switch:

var authenticationMethodVariable = AuthenticationMethod.FORMS;  // Set the "enum" value we want to use.
var methodName = authenticationMethodVariable.ToString();       // Get the user-friendly "name" of the "enum" value.

// Perform logic based on which "enum" value was chosen.
switch (authenticationMethodVariable.Value)
{
    case authenticationMethodVariable.Values.Forms: // Do something
        break;
    case authenticationMethodVariable.Values.Windows: // Do something
        break;
    case authenticationMethodVariable.Values.SSN: // Do something
        break;      
}

Более коротким решением было бы удалить перечисления {} и вместо этого вести статический счет того, сколько перечислений вы создали. Это также дает то преимущество, что вам не нужно добавлять новый экземпляр в список перечислений. например public static int nextAvailable { get; private set; } затем в конструкторе this.Value = nextAvailable++;

kjhf 06.02.2014 01:42

Интересная идея @kjhf. Однако меня беспокоит то, что если кто-то изменит порядок кода, значение, присвоенное значениям перечисления, также может измениться. Например, это может привести к получению неправильного значения перечисления, когда значение перечисления сохраняется в файл / базу данных, порядок строк «new AuthenticationMethod (...)» изменяется (например, одна удаляется), а затем снова запустить приложение и получить значение перечисления из файла / базы данных; значение перечисления может не соответствовать первоначально сохраненному AuthenticationMethod.

deadlydog 06.02.2014 01:50

Хороший момент - хотя я надеюсь, что в этих конкретных случаях люди не будут полагаться на целочисленное значение перечисления (или переупорядочивать код перечисления.) - и это значение используется исключительно как переключатель и, возможно, альтернатива .Equals () и. GetHashCode (). Если вас беспокоит, вы всегда можете добавить большой комментарий с надписью «НЕ ПЕРЕЗАКАЗАТЬ»: p

kjhf 06.02.2014 02:30

Разве нельзя просто перегрузить оператора =, чтобы переключение заработало? Я сделал это в VB и теперь могу использовать это в операторе select case.

user1318499 19.08.2014 14:38

@ user1318499 Нет, C# имеет более строгие правила в отношении оператора switch, чем VB. Вы не можете использовать экземпляры класса для оператора Case; вы можете использовать только постоянные примитивы.

deadlydog 20.08.2014 02:22

Я с Харви, но не использую const. Я могу смешивать и сопоставлять строку, int, что угодно.

public class xlsLayout
{
    public int xlHeaderRow = 1;
    public int xlFirstDataRow = 2;
    public int xlSkipLinesBetweenFiles = 1; //so 0 would mean don't skip
    public string xlFileColumn = "A";
    public string xlFieldColumn = "B";
    public string xlFreindlyNameColumn = "C";
    public string xlHelpTextColumn = "D";
}

Тогда позже ...

public partial class Form1 : Form
{
    xlsLayout xlLayout = new xlsLayout();

    xl.SetCell(xlLayout.xlFileColumn, xlLayout.xlHeaderRow, "File Name");
    xl.SetCell(xlLayout.xlFieldColumn, xlLayout.xlHeaderRow, "Code field name");
    xl.SetCell(xlLayout.xlFreindlyNameColumn, xlLayout.xlHeaderRow, "Freindly name");
    xl.SetCell(xlLayout.xlHelpTextColumn, xlLayout.xlHeaderRow, "Inline Help Text");
}

Я также использую Харви, но я использую модификатор доступа const, потому что это значение времени компиляции, и поэтому я могу использовать их в операторах switch. Использование вашего кода может работать в определенных ситуациях, но вы не можете использовать свои значения в switch.

Jeff Johnson 17.06.2014 17:59

Когда я нахожусь в подобной ситуации, я предлагаю решение ниже.

И в качестве потребляющего класса вы могли бы иметь

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MyApp.Dictionaries
{
    class Greek
    {

        public static readonly string Alpha = "Alpha";
        public static readonly string Beta = "Beta";
        public static readonly string Gamma = "Gamma";
        public static readonly string Delta = "Delta";


        private static readonly BiDictionary<int, string> Dictionary = new BiDictionary<int, string>();


        static Greek() {
            Dictionary.Add(1, Alpha);
            Dictionary.Add(2, Beta);
            Dictionary.Add(3, Gamma);
            Dictionary.Add(4, Delta);
        }

        public static string getById(int id){
            return Dictionary.GetByFirst(id);
        }

        public static int getByValue(string value)
        {
            return Dictionary.GetBySecond(value);
        }

    }
}

И используя двунаправленный словарь: На основе этого (https://stackoverflow.com/a/255638/986160) предполагается, что ключи будут связаны с отдельными значениями в словаре и аналогичны (https://stackoverflow.com/a/255630/986160), но немного более элегантно. Этот словарь также можно перечислить, и вы можете переходить от целых чисел к строкам. Также вам не обязательно иметь строку в вашей кодовой базе, за исключением этого класса.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace MyApp.Dictionaries
{

    class BiDictionary<TFirst, TSecond> : IEnumerable
    {
        IDictionary<TFirst, TSecond> firstToSecond = new Dictionary<TFirst, TSecond>();
        IDictionary<TSecond, TFirst> secondToFirst = new Dictionary<TSecond, TFirst>();

        public void Add(TFirst first, TSecond second)
        {
            firstToSecond.Add(first, second);
            secondToFirst.Add(second, first);
        }

        public TSecond this[TFirst first]
        {
            get { return GetByFirst(first); }
        }

        public TFirst this[TSecond second]
        {
            get { return GetBySecond(second); }
        }

        public TSecond GetByFirst(TFirst first)
        {
            return firstToSecond[first];
        }

        public TFirst GetBySecond(TSecond second)
        {
            return secondToFirst[second];
        }

        public IEnumerator GetEnumerator()
        {
            return GetFirstEnumerator();
        }

        public IEnumerator GetFirstEnumerator()
        {
            return firstToSecond.GetEnumerator();
        }

        public IEnumerator GetSecondEnumerator()
        {
            return secondToFirst.GetEnumerator();
        }
    }
}

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

  1. Может использоваться в операторе switch, например switch (myEnum)
  2. Может использоваться в параметрах функции, например. foo (тип myEnum)
  3. Можно указать, например, myEnum.FirstElement
  4. Я могу использовать строки, например. foo ("FirstElement") == foo (myEnum.FirstElement)

1,2 и 4 на самом деле могут быть решены с помощью C# Typedef строки (поскольку строки можно переключать в C#)

3 можно решить с помощью статических константных строк. Итак, если у вас такие же потребности, это самый простой подход:

public sealed class Types
{

    private readonly String name;

    private Types(String name)
    {
        this.name = name;

    }

    public override String ToString()
    {
        return name;
    }

    public static implicit operator Types(string str)
    {
        return new Types(str);

    }
    public static implicit operator string(Types str)
    {
        return str.ToString();
    }


    #region enum

    public const string DataType = "Data";
    public const string ImageType = "Image";
    public const string Folder = "Folder";
    #endregion

}

Это позволяет, например:

    public TypeArgs(Types SelectedType)
    {
        Types SelectedType = SelectedType
    }

а также

public TypeObject CreateType(Types type)
    {
        switch (type)
        {

            case Types.ImageType:
              //
                break;

            case Types.DataType:
             //
                break;

        }
    }

Где CreateType можно вызвать со строкой или типом. Однако недостатком является то, что любая строка автоматически является допустимым перечислением., это можно изменить, но тогда для этого потребуется какая-то функция инициализации ... или, возможно, сделать их явным внутренним приведением?

Теперь, если для вас было важно значение int (возможно, для скорости сравнения), вы, мог, используете некоторые идеи из фантастического ответа Якуба Штурца и делаете что-то безумное немного, это мой удар:

    public sealed class Types
{
    private static readonly Dictionary<string, Types> strInstance = new Dictionary<string, Types>();
    private static readonly Dictionary<int, Types> intInstance = new Dictionary<int, Types>();

    private readonly String name;
    private static int layerTypeCount = 0;
    private int value;
    private Types(String name)
    {
        this.name = name;
        value = layerTypeCount++;
        strInstance[name] = this;
        intInstance[value] = this;
    }

    public override String ToString()
    {
        return name;
    }


    public static implicit operator Types(int val)
    {
        Types result;
        if (intInstance.TryGetValue(val, out result))
            return result;
        else
            throw new InvalidCastException();
    }

    public static implicit operator Types(string str)
    {
        Types result;
        if (strInstance.TryGetValue(str, out result))
        {
            return result;
        }
        else
        {
            result = new Types(str);
            return result;
        }

    }
    public static implicit operator string(Types str)
    {
        return str.ToString();
    }

    public static bool operator ==(Types a, Types b)
    {
        return a.value == b.value;
    }
    public static bool operator !=(Types a, Types b)
    {
        return a.value != b.value;
    }

    #region enum

    public const string DataType = "Data";
    public const string ImageType = "Image";

    #endregion

}

но, конечно, «Типы bob = 4;» было бы бессмысленно, если бы вы не инициализировали их сначала, что могло бы как бы победить точку ...

Но теоретически TypeA == TypeB будет быстрее ...

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

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

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

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

if (bla == AuthenticationMethod.FORMS)

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

если вы используете «const» вместо «static readonly», тогда вы можете использовать значения как метки case в операторе switch.

Ed N. 01.08.2017 17:33

Очень простое решение с .Net 4.0 и выше. Никакой другой код не нужен.

public enum MyStatus
{
    Active = 1,
    Archived = 2
}

Чтобы получить строку, просто используйте:

MyStatus.Active.ToString("f");

или же

MyStatus.Archived.ToString("f");`

Значение будет «Активно» или «В архиве».

Чтобы увидеть различные строковые форматы (буква «f» сверху) при вызове Enum.ToString, см. Эту страницу Строки формата перечисления

Если я правильно вас понял, вы можете просто использовать .ToString () для извлечения имени перечисления из значения (при условии, что оно уже приведено как Enum); Если у вас был голый int (скажем, из базы данных или что-то в этом роде), вы можете сначала передать его в перечисление. Оба метода ниже дадут вам имя перечисления.

AuthenticationMethod myCurrentSetting = AuthenticationMethod.FORMS;
Console.WriteLine(myCurrentSetting); // Prints: FORMS
string name = Enum.GetNames(typeof(AuthenticationMethod))[(int)myCurrentSetting-1];
Console.WriteLine(name); // Prints: FORMS

Однако имейте в виду, что второй метод предполагает, что вы используете целые числа, а ваш индекс основан на 1 (а не на 0). Функция GetNames также довольно тяжелая для сравнения, вы генерируете целый массив каждый раз, когда она вызывается. Как вы можете видеть в первом методе, .ToString () на самом деле вызывается неявно. Оба они, конечно, уже упоминаются в ответах, я просто пытаюсь прояснить различия между ними.

старый пост, но ...

Ответ на этот вопрос может быть очень простым. Используйте функцию Enum.ToString ()

Есть 6 перегрузок этой функции, вы можете использовать Enum.Tostring ("F") или Enum.ToString () для возврата строкового значения. Не нужно беспокоиться ни о чем другом. Вот рабочая демонстрация

Обратите внимание, что это решение может работать не для всех компиляторов (эта демонстрация не работает должным образом), но, по крайней мере, оно работает для последней версии компилятора.

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

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

Во-первых, вы должны рассмотреть регистр ваших имен перечислений:

public enum AuthenticationMethod
{
    Forms = 1,
    WindowsAuthentication = 2,
    SingleSignOn = 3
}

Затем используйте это расширение:

using System.Text.RegularExpression;

public static class AnExtension
{
    public static string Name(this Enum value)
    {
        string strVal = value.ToString();
        try
        {
            return Regex.Replace(strVal, "([a-z])([A-Z])", "$1 $2");
        }
        catch
        {
        }
        return strVal;
    }
}

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

AuthenticationMethod am = AuthenticationMethod.WindowsAuthentication;
MessageBox.Show(am.Name());

Если вы читаете исходный вопрос, он хочет, чтобы другое отображение, например SingleSignOn , стало SSO. Ваше решение не работает.

Svek 07.02.2019 09:27

Для больших наборов перечислений строк перечисленные примеры могут стать утомительными. Если вам нужен список кодов состояния или список других перечислений на основе строк, использование системы атрибутов раздражает, а статический класс с экземплярами самого себя раздражает при настройке. В своем собственном решении я использую шаблоны T4, чтобы упростить создание перечислений с поддержкой строк. Результат похож на то, как работает класс HttpMethod.

Вы можете использовать это так:

    string statusCode = ResponseStatusCode.SUCCESS; // Automatically converts to string when needed
    ResponseStatusCode codeByValueOf = ResponseStatusCode.ValueOf(statusCode); // Returns null if not found

    // Implements TypeConverter so you can use it with string conversion methods.
    var converter = System.ComponentModel.TypeDescriptor.GetConverter(typeof(ResponseStatusCode));
    ResponseStatusCode code = (ResponseStatusCode) converter.ConvertFromInvariantString(statusCode);

    // You can get a full list of the values
    bool canIterateOverValues = ResponseStatusCode.Values.Any(); 

    // Comparisons are by value of the "Name" property. Not by memory pointer location.
    bool implementsByValueEqualsEqualsOperator = "SUCCESS" == ResponseStatusCode.SUCCESS; 

Вы начинаете с файла Enum.tt.

<#@ include file = "StringEnum.ttinclude" #>


<#+
public static class Configuration
{
    public static readonly string Namespace = "YourName.Space";
    public static readonly string EnumName = "ResponseStatusCode";
    public static readonly bool IncludeComments = true;

    public static readonly object Nodes = new
    {
        SUCCESS = "The response was successful.",
        NON_SUCCESS = "The request was not successful.",
        RESOURCE_IS_DISCONTINUED = "The resource requested has been discontinued and can no longer be accessed."
    };
}
#>

Затем вы добавляете свой файл StringEnum.ttinclude.

<#@ template debug = "false" hostspecific = "false" language = "C#" #>
<#@ assembly name = "System.Core" #>
<#@ import namespace = "System" #>
<#@ import namespace = "System.Linq" #>
<#@ import namespace = "System.Text" #>
<#@ import namespace = "System.Reflection" #>
<#@ import namespace = "System.Collections.Generic" #>
<#@ output extension = ".cs" #>
<#@ CleanupBehavior processor = "T4VSHost" CleanupAfterProcessingtemplate = "true" #>

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

using System;
using System.Linq;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;

namespace <#= Configuration.Namespace #>
{
    /// <summary>
    /// TypeConverter implementations allow you to use features like string.ToNullable(T).
    /// </summary>
    public class <#= Configuration.EnumName #>TypeConverter : TypeConverter
    {
        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
        }

        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
        {
            var casted = value as string;

            if (casted != null)
            {
                var result = <#= Configuration.EnumName #>.ValueOf(casted);
                if (result != null)
                {
                    return result;
                }
            }

            return base.ConvertFrom(context, culture, value);
        }

        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        {
            var casted = value as <#= Configuration.EnumName #>;
            if (casted != null && destinationType == typeof(string))
            {
                return casted.ToString();
            }

            return base.ConvertTo(context, culture, value, destinationType);
        }
    }

    [TypeConverter(typeof(<#= Configuration.EnumName #>TypeConverter))]
    public class <#= Configuration.EnumName #> : IEquatable<<#= Configuration.EnumName #>>
    {
//---------------------------------------------------------------------------------------------------
// V A L U E S _ L I S T
//---------------------------------------------------------------------------------------------------
<# Write(Helpers.PrintEnumProperties(Configuration.Nodes)); #>

        private static List<<#= Configuration.EnumName #>> _list { get; set; } = null;
        public static List<<#= Configuration.EnumName #>> ToList()
        {
            if (_list == null)
            {
                _list = typeof(<#= Configuration.EnumName #>).GetFields().Where(x => x.IsStatic && x.IsPublic && x.FieldType == typeof(<#= Configuration.EnumName #>))
                    .Select(x => x.GetValue(null)).OfType<<#= Configuration.EnumName #>>().ToList();
            }

            return _list;
        }

        public static List<<#= Configuration.EnumName #>> Values()
        {
            return ToList();
        }

        /// <summary>
        /// Returns the enum value based on the matching Name of the enum. Case-insensitive search.
        /// </summary>
        /// <param name = "key"></param>
        /// <returns></returns>
        public static <#= Configuration.EnumName #> ValueOf(string key)
        {
            return ToList().FirstOrDefault(x => string.Compare(x.Name, key, true) == 0);
        }


//---------------------------------------------------------------------------------------------------
// I N S T A N C E _ D E F I N I T I O N
//---------------------------------------------------------------------------------------------------      
        public string Name { get; private set; }
        public string Description { get; private set; }
        public override string ToString() { return this.Name; }

        /// <summary>
        /// Implcitly converts to string.
        /// </summary>
        /// <param name = "d"></param>
        public static implicit operator string(<#= Configuration.EnumName #> d)
        {
            return d.ToString();
        }

        /// <summary>
        /// Compares based on the == method. Handles nulls gracefully.
        /// </summary>
        /// <param name = "a"></param>
        /// <param name = "b"></param>
        /// <returns></returns>
        public static bool operator !=(<#= Configuration.EnumName #> a, <#= Configuration.EnumName #> b)
        {
            return !(a == b);
        }

        /// <summary>
        /// Compares based on the .Equals method. Handles nulls gracefully.
        /// </summary>
        /// <param name = "a"></param>
        /// <param name = "b"></param>
        /// <returns></returns>
        public static bool operator ==(<#= Configuration.EnumName #> a, <#= Configuration.EnumName #> b)
        {
            return a?.ToString() == b?.ToString();
        }

        /// <summary>
        /// Compares based on the .ToString() method
        /// </summary>
        /// <param name = "o"></param>
        /// <returns></returns>
        public override bool Equals(object o)
        {
            return this.ToString() == o?.ToString();
        }

        /// <summary>
        /// Compares based on the .ToString() method
        /// </summary>
        /// <param name = "other"></param>
        /// <returns></returns>
        public bool Equals(<#= Configuration.EnumName #> other)
        {
            return this.ToString() == other?.ToString();
        }

        /// <summary>
        /// Compares based on the .Name property
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return this.Name.GetHashCode();
        }
    }
}

<#+

public static class Helpers
{
        public static string PrintEnumProperties(object nodes)
        {
            string o = "";
            Type nodesTp = Configuration.Nodes.GetType();
            PropertyInfo[] props = nodesTp.GetProperties().OrderBy(p => p.Name).ToArray();

            for(int i = 0; i < props.Length; i++)
            {
                var prop = props[i];
                if (Configuration.IncludeComments)
                {
                    o += "\r\n\r\n";
                    o += "\r\n        ///<summary>";
                    o += "\r\n        /// "+Helpers.PrintPropertyValue(prop, Configuration.Nodes);
                    o += "\r\n        ///</summary>";
                }

                o += "\r\n        public static readonly "+Configuration.EnumName+" "+prop.Name+ " = new "+Configuration.EnumName+"(){ Name = \""+prop.Name+"\", Description = "+Helpers.PrintPropertyValue(prop, Configuration.Nodes)+ "};";
            }

            o += "\r\n\r\n";

            return o;
        }

        private static Dictionary<string, string> GetValuesMap()
        {
            Type nodesTp = Configuration.Nodes.GetType();
            PropertyInfo[] props= nodesTp.GetProperties();
            var dic = new Dictionary<string,string>();
            for(int i = 0; i < props.Length; i++)
            {
                var prop = nodesTp.GetProperties()[i];
                dic[prop.Name] = prop.GetValue(Configuration.Nodes).ToString();
            }
            return dic;
        }

        public static string PrintMasterValuesMap(object nodes)
        {
            Type nodesTp = Configuration.Nodes.GetType();
            PropertyInfo[] props= nodesTp.GetProperties();
            string o = "        private static readonly Dictionary<string, string> ValuesMap = new Dictionary<string, string>()\r\n        {";
            for(int i = 0; i < props.Length; i++)
            {
                var prop = nodesTp.GetProperties()[i];
                o += "\r\n            { \""+prop.Name+"\", "+(Helpers.PrintPropertyValue(prop,Configuration.Nodes)+" },");
            }
            o += ("\r\n        };\r\n");

            return o;
        }


        public static string PrintPropertyValue(PropertyInfo prop, object objInstance)
        {
            switch(prop.PropertyType.ToString()){
                case "System.Double":
                    return prop.GetValue(objInstance).ToString()+"D";
                case "System.Float":
                    return prop.GetValue(objInstance).ToString()+"F";
                case "System.Decimal":
                    return prop.GetValue(objInstance).ToString()+"M";
                case "System.Long":
                    return prop.GetValue(objInstance).ToString()+"L";
                case "System.Boolean":
                case "System.Int16":
                case "System.Int32":
                    return prop.GetValue(objInstance).ToString().ToLowerInvariant();
                case "System.String":
                    return "\""+prop.GetValue(objInstance)+"\"";
            }

            return prop.GetValue(objInstance).ToString();
        }

        public static string _ (int numSpaces)
        {
            string o = "";
            for(int i = 0; i < numSpaces; i++){
                o += " ";
            }

            return o;
        }
}
#>

Наконец, вы перекомпилируете файл Enum.tt, и результат будет выглядеть следующим образом:

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

using System;
using System.Linq;
using System.Collections.Generic;

namespace YourName.Space
{
    public class ResponseStatusCode
    {
//---------------------------------------------------------------------------------------------------
// V A L U E S _ L I S T 
//---------------------------------------------------------------------------------------------------



        ///<summary>
        /// "The response was successful."
        ///</summary>
        public static readonly ResponseStatusCode SUCCESS = new ResponseStatusCode(){ Name = "SUCCESS", Description = "The response was successful."};


        ///<summary>
        /// "The request was not successful."
        ///</summary>
        public static readonly ResponseStatusCode NON_SUCCESS = new ResponseStatusCode(){ Name = "NON_SUCCESS", Description = "The request was not successful."};


        ///<summary>
        /// "The resource requested has been discontinued and can no longer be accessed."
        ///</summary>
        public static readonly ResponseStatusCode RESOURCE_IS_DISCONTINUED = new ResponseStatusCode(){ Name = "RESOURCE_IS_DISCONTINUED", Description = "The resource requested has been discontinued and can no longer be accessed."};


        private static List<ResponseStatusCode> _list { get; set; } = null;
        public static List<ResponseStatusCode> ToList()
        {
            if (_list == null)
            {
                _list = typeof(ResponseStatusCode).GetFields().Where(x => x.IsStatic && x.IsPublic && x.FieldType == typeof(ResponseStatusCode))
                    .Select(x => x.GetValue(null)).OfType<ResponseStatusCode>().ToList();
            }

            return _list;
        }

        public static List<ResponseStatusCode> Values()
        {
            return ToList();
        }

        /// <summary>
        /// Returns the enum value based on the matching Name of the enum. Case-insensitive search.
        /// </summary>
        /// <param name = "key"></param>
        /// <returns></returns>
        public static ResponseStatusCode ValueOf(string key)
        {
            return ToList().FirstOrDefault(x => string.Compare(x.Name, key, true) == 0);
        }


//---------------------------------------------------------------------------------------------------
// I N S T A N C E _ D E F I N I T I O N 
//---------------------------------------------------------------------------------------------------       
        public string Name { get; set; }
        public string Description { get; set; }
        public override string ToString() { return this.Name; }

        /// <summary>
        /// Implcitly converts to string.
        /// </summary>
        /// <param name = "d"></param>
        public static implicit operator string(ResponseStatusCode d)
        {
            return d.ToString();
        }

        /// <summary>
        /// Compares based on the == method. Handles nulls gracefully.
        /// </summary>
        /// <param name = "a"></param>
        /// <param name = "b"></param>
        /// <returns></returns>
        public static bool operator !=(ResponseStatusCode a, ResponseStatusCode b)
        {
            return !(a == b);
        }

        /// <summary>
        /// Compares based on the .Equals method. Handles nulls gracefully.
        /// </summary>
        /// <param name = "a"></param>
        /// <param name = "b"></param>
        /// <returns></returns>
        public static bool operator ==(ResponseStatusCode a, ResponseStatusCode b)
        {
            return a?.ToString() == b?.ToString();
        }

        /// <summary>
        /// Compares based on the .ToString() method
        /// </summary>
        /// <param name = "o"></param>
        /// <returns></returns>
        public override bool Equals(object o)
        {
            return this.ToString() == o?.ToString();
        }

        /// <summary>
        /// Compares based on the .Name property
        /// </summary>
        /// <returns></returns>
        public override int GetHashCode()
        {
            return this.Name.GetHashCode();
        }
    }
}
Enum.GetName(typeof(MyEnum), (int)MyEnum.FORMS)
Enum.GetName(typeof(MyEnum), (int)MyEnum.WINDOWSAUTHENTICATION)
Enum.GetName(typeof(MyEnum), (int)MyEnum.SINGLESIGNON)

выходы:

"ФОРМЫ"

"WINDOWSAUTHENTICATION"

"ЕДИНАЯ ТОЧКА ВХОДА"

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

using System;
using System.Collections.Generic;

namespace console_test
{
    class Program
    {
        #region SaveFormat
        internal enum SaveFormat
        {
            DOCX,
            PDF
        }

        internal static Dictionary<SaveFormat,string> DictSaveFormat = new Dictionary<SaveFormat, string>
        {
            { SaveFormat.DOCX,"This is value for DOCX enum item" },
            { SaveFormat.PDF,"This is value for PDF enum item" }
        };

        internal static void enum_value_test(SaveFormat save_format)
        {
            Console.WriteLine(DictSaveFormat[save_format]);
        }
        #endregion

        internal static void Main(string[] args)
        {
            enum_value_test(SaveFormat.DOCX);//Output: This is value for DOCX enum item
            Console.Write("Press any key to continue . . . ");
            Console.ReadKey(true);
        }
    }
}

Я создал базовый класс для создания перечислений со строковыми значениями в .NET. Это всего лишь один файл C#, который вы можете скопировать и вставить в свои проекты или установить с помощью пакета NuGet с именем StringEnum. Репозиторий GitHub

  • Intellisense предложит имя перечисления, если класс аннотирован xml-комментарием <completitionlist>. (Работает как на C#, так и на VB)

Intellisense demo

  • Использование аналогично обычному перечислению:
///<completionlist cref = "HexColor"/> 
class HexColor : StringEnum<HexColor>
{
    public static readonly HexColor Blue = Create("#FF0000");
    public static readonly HexColor Green = Create("#00FF00");
    public static readonly HexColor Red = Create("#000FF");
}
    // Static Parse Method
    HexColor.Parse("#FF0000") // => HexColor.Red
    HexColor.Parse("#ff0000", caseSensitive: false) // => HexColor.Red
    HexColor.Parse("invalid") // => throws InvalidOperationException

    // Static TryParse method.
    HexColor.TryParse("#FF0000") // => HexColor.Red
    HexColor.TryParse("#ff0000", caseSensitive: false) // => HexColor.Red
    HexColor.TryParse("invalid") // => null

    // Parse and TryParse returns the preexistent instances
    object.ReferenceEquals(HexColor.Parse("#FF0000"), HexColor.Red) // => true

    // Conversion from your `StringEnum` to `string`
    string myString1 = HexColor.Red.ToString(); // => "#FF0000"
    string myString2 = HexColor.Red; // => "#FF0000" (implicit cast)

Установка:

  • Вставьте следующий базовый класс StringEnum в свой проект. (Последняя версия)
  • Или установите пакет NuGet StringEnum, который основан на .Net Standard 1.0, поэтому он работает на .Net Core> = 1.0, .Net Framework> = 4.5, Mono> = 4.6 и т. д.
    /// <summary>
    /// Base class for creating string-valued enums in .NET.<br/>
    /// Provides static Parse() and TryParse() methods and implicit cast to string.
    /// </summary>
    /// <example> 
    /// <code>
    /// class Color : StringEnum &lt;Color&gt;
    /// {
    ///     public static readonly Color Blue = Create("Blue");
    ///     public static readonly Color Red = Create("Red");
    ///     public static readonly Color Green = Create("Green");
    /// }
    /// </code>
    /// </example>
    /// <typeparam name = "T">The string-valued enum type. (i.e. class Color : StringEnum&lt;Color&gt;)</typeparam>
    public abstract class StringEnum<T> : IEquatable<T> where T : StringEnum<T>, new()
    {
        protected string Value;
        private static Dictionary<string, T> valueDict = new Dictionary<string, T>();
        protected static T Create(string value)
        {
            if (value == null)
                return null; // the null-valued instance is null.

            var result = new T() { Value = value };
            valueDict.Add(value, result);
            return result;
        }

        public static implicit operator string(StringEnum<T> enumValue) => enumValue.Value;
        public override string ToString() => Value;

        public static bool operator !=(StringEnum<T> o1, StringEnum<T> o2) => o1?.Value != o2?.Value;
        public static bool operator ==(StringEnum<T> o1, StringEnum<T> o2) => o1?.Value == o2?.Value;

        public override bool Equals(object other) => this.Value.Equals((other as T)?.Value ?? (other as string));
        bool IEquatable<T>.Equals(T other) => this.Value.Equals(other.Value);
        public override int GetHashCode() => Value.GetHashCode();

        /// <summary>
        /// Parse the <paramref name = "value"/> specified and returns a valid <typeparamref name = "T"/> or else throws InvalidOperationException.
        /// </summary>
        /// <param name = "value">The string value representad by an instance of <typeparamref name = "T"/>. Matches by string value, not by the member name.</param>
        /// <param name = "caseSensitive">If true, the strings must match case and takes O(log n). False allows different case but is little bit slower (O(n))</param>
        public static T Parse(string value, bool caseSensitive = true)
        {
            var result = TryParse(value, caseSensitive);
            if (result == null)
                throw new InvalidOperationException((value == null ? "null" : $"'{value}'") + $" is not a valid {typeof(T).Name}");

            return result;
        }

        /// <summary>
        /// Parse the <paramref name = "value"/> specified and returns a valid <typeparamref name = "T"/> or else returns null.
        /// </summary>
        /// <param name = "value">The string value representad by an instance of <typeparamref name = "T"/>. Matches by string value, not by the member name.</param>
        /// <param name = "caseSensitive">If true, the strings must match case. False allows different case but is slower: O(n)</param>
        public static T TryParse(string value, bool caseSensitive = true)
        {
            if (value == null) return null;
            if (valueDict.Count == 0) System.Runtime.CompilerServices.RuntimeHelpers.RunClassConstructor(typeof(T).TypeHandle); // force static fields initialization
            if (caseSensitive)
            {
                if (valueDict.TryGetValue(value, out T item))
                    return item;
                else
                    return null;
            }
            else
            {
                // slower O(n) case insensitive search
                return valueDict.FirstOrDefault(f => f.Key.Equals(value, StringComparison.OrdinalIgnoreCase)).Value;
                // Why Ordinal? => https://esmithy.net/2007/10/15/why-stringcomparisonordinal-is-usually-the-right-choice/
            }
        }
    }

Другие вопросы по теме