У меня есть следующее перечисление:
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);
Хорошо, теперь все это работает как шарм, но я считаю, что это много работы. Мне было интересно, есть ли для этого лучшее решение.
Я также пробовал что-то со словарем и статическими свойствами, но это тоже было не лучше.
Это «Перечисление», а не «Перечислитель».
MSDN рекомендует классы атрибутов суффикса с суффиксом «Атрибут». Итак "класс StringValueAttribute";)
Я согласен с @BenAlabaster, на самом деле это довольно гибко. Кроме того, вы можете сделать это методом расширения, просто добавив this перед Enum в вашем статическом методе. Тогда можно делать AuthenticationMethod.Forms.GetStringValue();
Этот подход использует отражение для чтения значений атрибутов, и, по моему опыту, он очень медленный, если вам приходится вызывать GetStringValue () много раз. Шаблон type-safe-enum работает быстрее.
Если вы все же идете по маршруту атрибутов, вместо того, чтобы прокручивать свой собственный, вы можете использовать System.ComponentModel.DescriptionAttribute.
Почему вы испытываете все эти проблемы, если вы можете просто вызвать .ToString() в перечислении, чтобы получить строку «FORMS»?





Метод использования
Enum.GetName(Type MyEnumType, object enumvariable)
как в (Предположим, что Shipper - это определенное Enum)
Shipper x = Shipper.FederalExpress;
string s = Enum.GetName(typeof(Shipper), x);
В классе Enum есть множество других статических методов, которые стоит изучить ...
Точно. Я сделал настраиваемый атрибут для строкового описания, но это потому, что мне нужна удобная для пользователя версия (с пробелами и другими специальными символами), которую можно легко привязать к ComboBox или тому подобному.
Enum.GetName отражает имена полей в перечислении - так же, как .ToString (). Если производительность является проблемой, это может быть проблемой. Я бы не стал беспокоиться об этом, если вы не конвертируете множество перечислений.
Другой вариант, который следует рассмотреть, если вам нужно перечисление с дополнительной функциональностью, - это «свернуть свой собственный» с помощью структуры ... вы добавляете статические свойства с именами только для чтения для представления значений перечисления, которые инициализируются для конструкторов, которые генерируют отдельные экземпляры структуры ...
затем вы можете добавить любые другие члены структуры, которые захотите, чтобы реализовать любую функциональность, которую вы хотите, чтобы это "перечисление" имело ...
Проблема здесь в том, что GetName нельзя локализовать. Это не всегда вызывает беспокойство, но об этом следует знать.
@Charles: Ага, я сделал это для перечисляемых значений, которые требуют более сложной логики синтаксического анализа.
@Ed, да, это чрезвычайно полезный метод, с которым не многие знакомы
почему не Shipper.FederalExpress.ToString(). Я пробовал в C# 4.0, и результат был таким же, как Enum.GetName
Это работает только в том случае, если у вас явно есть конкретный член перечисления во время компиляции. Если все, что у вас есть, - это переменная, которой во время выполнения может быть назначен какой-либо член перечисления, вы не сможете этого сделать.
@tyler, и, поскольку он работает только во время компиляции, если вы знаете, какой член вам нужен, в таком случае почему бы вместо Shipper.FederalExpress.ToString() просто не поставить "FederalExpress"?
@CharlesBretana, я не понимаю твоей точки зрения. Мне не обязательно знать, кого я ищу. Предположим, что это переменная с именем x (точно такая же, как в вашем ответе), разве x.toString() не работает? Я не пробовал, потому что прошло много времени с тех пор, как я написал последнюю строчку C#. если это не сработает, тогда я понимаю вашу точку зрения.
@tyler, мне нужно сделать это сейчас, чтобы быть уверенным, но, насколько я помню, если вы не переопределите это (что вы не можете сделать для перечисления), ToString() для переменной просто возвращает имя типа. Это одна из причин, по которой вы по привычке переопределяете ToString() для других объявленных типов, чтобы отладчик отображал что-то полезное (а не только имя типа) при просмотре содержимого коллекций и тому подобного.
К сожалению, отражение для получения атрибутов в перечислениях происходит довольно медленно:
См. Этот вопрос: Кто-нибудь знает, как быстро перейти к настраиваемым атрибутам значения перечисления?
.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>, чтобы сохранить декларативный аспект.
Да, именно это мы и сделали в приложении с множеством перечислений, когда обнаружили, что отражение было узким местом.
Спасибо, Джон и Кейт, в итоге я воспользовался вашим предложением из Словаря. Отлично работает (и быстро!).
@JonSkeet Я знаю, что это старое. Но как этого добиться?
@ user919426: Достичь хочешь? Помещать их в словарь? Просто создайте словарь, в идеале с инициализатором коллекции ... непонятно, о чем вы просите.
Спасибо за ответ @JonSkeet. Извините, я хотел спросить о фактической реализации. Как "получить один раз" без повторного звонка. Воспользуемся вашим ответом, чтобы понять логику.
nameof() работает, только если вы заранее знаете экземпляр enum. т.е. в var enumVal = MyEnum.Value; значение, возвращаемое nameof(enumVal), будет «enumVal», а не «Value». Это функция компилятора, а не среда выполнения.
@JoeBrockhaus Да, хотя здесь нет экземпляра - nameof(MyEnum.Value) станет "Value" во время компиляции, экземпляра MyEnum нет.
@Keith Не уверен, что я понимаю - этот вопрос (и ответ) касается получения имени экземпляра перечисления. Кто-то может легко прийти сюда, прочитать этот ответ, увидеть упоминание о том, что nameof () является «самым быстрым способом получения имен перечислений», и использовать его, думая, что они получат желаемые результаты. Я знаю, что ваш пример не показывает этот экземпляр, но, возможно, стоит отредактировать ответ, чтобы уточнить.
@JoeBrockhaus, похоже, все примеры в вопросе получают имя явного перечисления, например (из вопроса) StringEnum.GetStringValue(AuthenticationMethod.FORMS) - в этом контексте nameof(AuthenticationMethod.FORMS) быстрее. Я совершенно четко ответил, что это строго оптимизация времени компиляции, но я добавлю пояснение.
Вы можете ссылаться на имя, а не на значение, используя 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());
Это не поможет, если для «понятного имени» нужен пробел. Например, "Проверка подлинности с помощью форм"
Поэтому убедитесь, что перечисление названо с заглавными буквами, например FormsAuthentication, и вставьте пробел перед любыми заглавными буквами, которых нет в начале. Вставлять пробел в строку - это не ракетостроение ...
Автоматический интервал между именами Pascal Case становится проблематичным, если они содержат сокращения, которые должны быть написаны с заглавной буквы, например XML или GPS.
@RichardEv, для этого не существует идеального регулярного выражения, но вот одно, которое должно работать немного лучше с сокращениями. "(?!^)([^A-Z])([A-Z])", "$1 $2". Итак, HereIsATEST становится Here Is ATEST.
Не изящно делать эти маленькие "хаки", каковы они есть. Я понимаю, что говорит OP, и пытаюсь найти аналогичное решение, то есть используя элегантность Enums, но имея возможность легко получить доступ к связанному сообщению. Единственное решение, которое я могу придумать, - это применить какое-то сопоставление между именем перечисления и строковым значением, но это не решает проблему сохранения строковых данных (однако делает его практичным для сценариев, где вам нужно иметь несколько регионов и т. д. )
Попробуйте шаблон безопасное перечисление типов.
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>();
заполнение этого сопоставления в конструкторе экземпляра
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: Мне не нужно. Поскольку у нас есть только один экземпляр каждого AuthenticationMethod, ссылочное равенство, унаследованное от Object, работает нормально.
+1 Я использовал это решение раньше. Как мощное перечисление. Я полагаю, вы не можете использовать это в качестве значений в операторе switch, но можете ли? Кроме того, единственный раз, когда вам придется беспокоиться о проблеме равенства, это если он был объявлен как структура, а не как класс.
@InvisibleBacon: Верно, переключатель в этом случае не вариант. Однако иногда вы можете заменить switch полиморфизмом. См .: sourcemaking.com/refactoring/…
Это странно и кажется мне опасным ... Что мешает этому: AuthenticationMethod a = new AuthenticationMethod (); var b = a.FORMS.SINGLESIGNON; поскольку a.FORMS сам по себе является экземпляром AuthenticationMethod?
@tyriker: Компилятор умеет. Конструктор является частным, поэтому вы не можете создать новый экземпляр. Также статические члены недоступны через экземпляр.
@Jakub Очень интересно. Мне пришлось поиграть с ним, чтобы понять, как его использовать и понять его преимущества. Это общедоступный нестатический класс, но его нельзя создать, и вы можете получить доступ только к его статическим членам. По сути, он ведет себя как перечисление. Но лучшая часть ... статические члены типизированы для класса, а не для общей строки или int. Это ... [подождите] ... безопасное перечисление! Спасибо, что помогли мне понять.
Я вижу проблему, когда вы хотите смешать это с внешними значениями (например, DB). Вы хотите преобразовать это в строго типизированный, но не можете. Также невозможно написать цикл, который сопоставляет строку с правильным строго типизированным значением. Все, что вы можете сделать, это серия уродливых утверждений if else.
@ssg: Извините, я не понимаю вашей точки зрения. Вы можете реализовать явные / неявные операторы в классе AuthenticationMethod, если они вам действительно нужны.
@Jakub: Допустим, у вас 20 имен, и вы используете те же имена в БД. Вы читаете запись и хотите преобразовать ее в enum в коде. Вам нужно сопоставить каждую строку со строго типизированным эквивалентом, проверив серию ifs. Как здесь помогает перегрузка оператора?
@ssg: Я обновил образец. Итак, теперь, если вы можете написать var strongTypeInstance = (AuthenticationMethod) "FORMS"; Я не уверен насчет операторов преобразования. Возможно, статический метод Parse сработал бы лучше. Если хочешь, можешь получить.
Для получения дополнительной информации о том, как реализовать этот шаблон, взгляните на javacamp.org/designPattern/enum.html. Я знаю, что это не реализация C#, но все же понятно.
Мне нравится это решение - единственный серьезный недостаток, с которым я столкнулся, заключается в том, что оно не работает в операторе switch, то есть я могу использовать обычное перечисление или список констант в переключателе, но не эти.
Эээ ... Это просто вырожденное непересекающееся объединение, или размеченное объединение, или помеченное объединение en.wikipedia.org/wiki/Tagged_union. Это было с 1960-х, ребята. Не нужно придумывать яркое название для чего-то, что существует уже несколько десятилетий и уже имеет идеально подходящее название (пришедшее из математики).
@MauricioScheffer: Я могу спорить о вырождении, но вы правы. Все это было раньше, и все это повторится снова.
одно изменение, которое я бы внес в это, - хранить строковые значения не как поля экземпляра, а как статические константы. Зачем тратить хранилище? Просто поместите оператор switch case в ToString (); тогда строковые значения сохраняются только в определении типа, а не в каждом экземпляре «перечисления». (тип). Вы даже можете просто жестко запрограммировать их в операторах case переключателя. Кроме того, это яркий пример ситуации, когда вы должны использовать структуру вместо класса. Это (или должно быть) в основном неизменяемое.
@CharlesBretana: Вы говорите о переменной private static readonly Dictionary<string, AuthenticationMethod> instance? И, пожалуйста, подробнее о структуре. Я не уверен, что ты сможешь сделать это таким гениальным ...
Что мне нравится в этом, так это то, что вы также можете определить дополнительные методы в перечислении, чтобы дать такие вещи, как групповое поведение (например, стандартный пример перечисления DayOfTheWeek с методом IsWeekend, который возвращает true, если экземпляр, на который ссылается, суббота или воскресенье).
Те, кто обеспокоен тем, что сбивают с толку более поздних кодировщиков ... Вы можете использовать слово «Enum» в имени класса, чтобы побудить программистов-пользователей просмотреть исходный код, если он доступен.
Это хороший шаблон, но у меня проблемы с сериализацией ... Чтобы реализовать ISerializable, ваш класс должен включать специальный конструктор, который нарушит запечатанный / одноэлементный характер кода. Я также пытаюсь использовать этот TypeSafeEnum в свойстве UserControl - например, этот <uc:MyUC AuthMethod = "FORMS" /> - поэтому мне пришлось реализовать TypeConverter, и это, похоже, вызывает множество других проблем, одна из которых заключается в том, что статический конструктор не запускается до того, как TypeConverter попытается выполнить преобразования. (Обходной путь: просто сохраните опору как строку и примените при необходимости)
Поскольку значения type-safe-enum не являются константами времени компиляции (как обычные значения enum и строки), вы не можете использовать их везде, где требуются значения констант времени компиляции. В моем случае это были атрибуты, но я представляю еще кое-что. switch, однако, не проблема, поскольку вы можете реализовать такие вещи в классе type-safe-enum, таким образом сделав его умным перечислением.
@ JakubŠturc На случай, если вы ответите на это: Как мне перечислить это как enum Enum.GetValues()? Единственное, о чем я могу думать, - это отражение в статической функции Enumerate().
@ JakubŠturc ссылка javacamp.org/designPattern/enum.html больше не работает.
Мне нравится этот шаблон, и я использовал его раньше во многих формах, но, возможно, одна вещь, которая возвращает меня к Перечисления, - это то, что я не могу использую этот TypeSafeEnum как значение по умолчанию для параметра, поскольку это не константа времени компиляции. Есть идеи, как обойти эту икоту?
Работает до тех пор, пока значение не нужно сериализовать.
@SkyFighter, если вы посмотрите на версию deadlydog, которая вместо этого делает int общедоступным перечислением, вы можете использовать это для значений по умолчанию или в операторе switch.
Изначально я использовал вашу реализацию, но подумал, что мне неприятно писать так много кода вручную, поэтому я создал несколько шаблонов T4, чтобы помочь с этим. stackoverflow.com/a/52209481/1582837 Он предоставляет способы перебора значений с помощью Enum.Values. Может также помочь со значениями по умолчанию для параметров. @MikeH
Я использую атрибут 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.??() или что-то в этом роде?
Этот ответ был написан около десяти лет назад, поэтому я действительно не уверен в исходном контексте. Проблема с SO в том, что он существует вечно. Я считаю, что это было связано с добавлением данных, которые должны отображаться в пользовательских интерфейсах, таких как WinForms или WPF, и в этом случае простая привязка свойства или коллекции к элементу управления пользовательского интерфейса напрямую подберет преобразователь типов.
Когда я сталкиваюсь с этой проблемой, есть несколько вопросов, на которые я пытаюсь найти ответы в первую очередь:
Самый простой способ сделать это - использовать 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 для получения дополнительной информации о вашем бубу
Я хотел опубликовать это как комментарий к сообщению, цитируемому ниже, но не смог, потому что у меня недостаточно репутации, поэтому, пожалуйста, не голосуйте против. Код содержал ошибку, и я хотел указать на это людям, пытающимся использовать это решение:
[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
Вам следует пересмотреть этот ответ, включив в него пример, а не просто ссылку.
и прочтите вопрос, тот же ответ, что и выше msdn.microsoft.com/en-us/library/system.enum.getname.aspx
Подход, который я нашел для интернационализации перечислений или получения текста перечислений из соответствующих файлов ресурсов, заключается в создании класса атрибута путем наследования класса 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/…
Мой вариант
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 хотел другое значение.
Разве вы не имеете в виду, когда звоните в AuthenticationMethod.FORMS.ToDescription()?
Если вы пришли сюда, чтобы реализовать простое 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.
consts не подходят для общедоступных классов, поскольку они запекаются во время компиляции, вы не можете заменить стороннюю DLL без перекомпиляции всего кода с помощью const. Смещение производительности consts vs static readonly незначительно.
Как я решил это как метод расширения:
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.
Берни, DescriptionAttribute находится в System.ComponentModel
Мой ответ, работая над ответом @ 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.
@Brent Потому что чаще всего у вас значение .ToString() отличается от необходимого вам удобного значения.
@Brent - потому что это отличается от задаваемого вопроса. Возникает вопрос, как получить эту строку из переменной, которой присвоено перечислимое значение. Это динамично во время выполнения. Это проверка определения типа и установки во время выполнения.
@Hogan - ToString () также работает с переменными: any fruit = any.Tomato;string tomato = fruit.ToString();
@LiborV - пожалуйста, помните, что это было написано в 09 - C# тогда был другим, и ToString () в экземпляре перечисления сделал что-то другое.
для меня прагматический подход - это класс внутри класса, образец:
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. Однако меня беспокоит то, что если кто-то изменит порядок кода, значение, присвоенное значениям перечисления, также может измениться. Например, это может привести к получению неправильного значения перечисления, когда значение перечисления сохраняется в файл / базу данных, порядок строк «new AuthenticationMethod (...)» изменяется (например, одна удаляется), а затем снова запустить приложение и получить значение перечисления из файла / базы данных; значение перечисления может не соответствовать первоначально сохраненному AuthenticationMethod.
Хороший момент - хотя я надеюсь, что в этих конкретных случаях люди не будут полагаться на целочисленное значение перечисления (или переупорядочивать код перечисления.) - и это значение используется исключительно как переключатель и, возможно, альтернатива .Equals () и. GetHashCode (). Если вас беспокоит, вы всегда можете добавить большой комментарий с надписью «НЕ ПЕРЕЗАКАЗАТЬ»: p
Разве нельзя просто перегрузить оператора =, чтобы переключение заработало? Я сделал это в VB и теперь могу использовать это в операторе select case.
@ user1318499 Нет, C# имеет более строгие правила в отношении оператора switch, чем VB. Вы не можете использовать экземпляры класса для оператора Case; вы можете использовать только постоянные примитивы.
Я с Харви, но не использую 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.
Когда я нахожусь в подобной ситуации, я предлагаю решение ниже.
И в качестве потребляющего класса вы могли бы иметь
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,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.
Очень простое решение с .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. Ваше решение не работает.
Для больших наборов перечислений строк перечисленные примеры могут стать утомительными. Если вам нужен список кодов состояния или список других перечислений на основе строк, использование системы атрибутов раздражает, а статический класс с экземплярами самого себя раздражает при настройке. В своем собственном решении я использую шаблоны 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
<completitionlist>. (Работает как на C#, так и на VB)
///<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)
.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 <Color>
/// {
/// 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<Color>)</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/
}
}
}
Хотя это может показаться вам длинным, на самом деле это довольно гибкий способ для других вещей. Как заметил один из моих коллег, это можно использовать во многих случаях для замены Enum Helpers, которые сопоставляют коды базы данных со значениями перечисления и т. д.