Как лучше всего преобразовать строку в значение перечисления в C#?
У меня есть тег выбора HTML, содержащий значения перечисления. Когда страница размещена, я хочу выбрать значение (которое будет в форме строки) и преобразовать его в соответствующее значение перечисления.
В идеальном мире я мог бы сделать что-то вроде этого:
StatusEnum MyStatus = StatusEnum.Parse("Active");
но это неверный код.





Вы ищете Enum.Parse.
SomeEnum enum = (SomeEnum)Enum.Parse(typeof(SomeEnum), "EnumValue");
В .NET Core и .NET Framework ≥4.0 есть общий метод синтаксического анализа:
Enum.TryParse("Active", out StatusEnum myStatus);
Это также включает в себя новые встроенные переменные out в C# 7, так что выполняется попытка синтаксического анализа, преобразование в явный тип перечисления и инициализация + заполнение переменной myStatus.
Если у вас есть доступ к C# 7 и последней версии .NET, это лучший способ.
В .NET это довольно некрасиво (до 4 и выше):
StatusEnum MyStatus = (StatusEnum) Enum.Parse(typeof(StatusEnum), "Active", true);
Я стараюсь это упростить:
public static T ParseEnum<T>(string value)
{
return (T) Enum.Parse(typeof(T), value, true);
}
Тогда я могу:
StatusEnum MyStatus = EnumUtil.ParseEnum<StatusEnum>("Active");
В комментариях предлагается один из вариантов - добавить расширение, что достаточно просто:
public static T ToEnum<T>(this string value)
{
return (T) Enum.Parse(typeof(T), value, true);
}
StatusEnum MyStatus = "Active".ToEnum<StatusEnum>();
Наконец, вы можете захотеть использовать перечисление по умолчанию, если строка не может быть проанализирована:
public static T ToEnum<T>(this string value, T defaultValue)
{
if (string.IsNullOrEmpty(value))
{
return defaultValue;
}
T result;
return Enum.TryParse<T>(value, true, out result) ? result : defaultValue;
}
Что делает это призывом:
StatusEnum MyStatus = "Active".ToEnum(StatusEnum.None);
Тем не менее, я был бы осторожен, добавляя такой метод расширения к string, поскольку (без контроля пространства имен) он будет отображаться во всех экземплярах string, независимо от того, содержат ли они перечисление или нет (поэтому 1234.ToString().ToEnum(StatusEnum.None) будет действительным, но бессмысленным). Часто лучше избегать загромождения основных классов Microsoft дополнительными методами, которые применяются только в очень специфических контекстах, если вся ваша команда разработчиков не очень хорошо понимает, что эти расширения делают.
@avinashr прав насчет ответа @McKenzieG1, но это не ВСЕГДА важно. Например, было бы бессмысленной микро-оптимизацией беспокоиться о синтаксическом анализе перечисления, если бы вы выполняли вызов БД для каждого синтаксического анализа.
сделайте это методом расширения, и он будет идеальным
@ H.M. Я не думаю, что здесь уместно расширение - это немного особый случай, и расширение будет применяться к строке каждый. Если бы вы действительно хотели это сделать, это было бы тривиальным изменением.
@keith, это было мое мнение, потому что статические классы очень трудно запомнить, но всегда следует помнить о методе расширения! и да, для параметра требуется только ключевое слово <code>this</code>
Как насчет Enum.TryParse?
@ H.M. Я добавил версию расширения, с предупреждением.
@Elaine Добавлен вариант, использующий TryParse.
@Keith: Не могли бы вы рассказать немного о том, что было бы против создания из этого метода расширения? Я не понимаю, в чем может быть проблема.
@bump да - как правило, вы должны быть очень осторожны, расширяя встроенные объекты Microsoft, особенно очень распространенные, такие как object или string, поскольку без тщательного контроля пространства имен это приведет к тому, что ваш метод расширения будет виден в каждом экземпляре всей вашей базы кода . Это не проблема, если метод всегда подходит (например, .Trim() или .Split()), но анализ перечисления будет применяться только тогда, когда ожидается, что строка будет содержать перечисление. Один или два таких метода, вероятно, подойдут, но вы можете столкнуться с множеством запутанных в больших проектах.
@Keith Хорошо, но это применимо только в том случае, если пространство имен включено в права использования? Значит, namespace SomeNameSpace.Extensions.Strings не вызовет никаких проблем?
@bump да, именно это я имею в виду под управлением пространством имен. Вы можете поместить эти расширения в пространства имен, чтобы разрешить их доступность - это баланс удобства и стандартизации кода, который во многом зависит от вашей команды разработчиков.
очень хорошо. вам нужна структура where T: в последнем примере.
@bbrik это не особо помогает - нам действительно нужно общее ограничение enum: stackoverflow.com/q/7244/905
Я не считаю расширение таким уж плохим. С помощью встроенных классов и т. д. Вы можете писать бессмысленный код независимо от расширений. Чтобы использовать расширение, вы должны включить его, а затем фактически использовать. Даже в случаях, следующих за предложенным в этих комментариях использованием расширений, вам все равно понадобится обработка ошибок в случае неправильного ввода, я не понимаю, чем это отличается. Очевидно, что вы должны поместить любые и все расширения, которые вы пишете, которые расширяют базовые классы, в ваше собственное пространство имен.
@Peter Я не сказал, что это плохо, просто нужно быть осторожным - расширение, похоже, применяется ко всем экземплярам строки, но имеет смысл только в подмножестве контекстов. Вы можете обрабатывать ошибочные вводы, но это проверка времени выполнения на наличие проблемы времени компиляции. Например, SubString() всегда применяется к строке, и во время выполнения вы проверяете, является ли строка слишком короткой для предоставленных индексов, но ToEnum может или не может применяться в зависимости от того, что вы вводите. Это неплохо или блокирует как таковое, просто что-то быть осторожным.
Я понимаю, что вы имеете в виду, возможно, если бы он назывался TryToEnum (), он был бы более явным и прояснил бы, что он может потерпеть неудачу. По сути, расширения - это сахар компилятора, метод расширения .ToEnum () ничем не отличается от Enum.Parse () - параметр строки не может быть гарантированно преобразован.
Как упоминалось в @bbrik, вам нужно добавить where T: struct в ваш последний пример, чтобы компилятор принял его.
@Keith - я хочу использовать метод расширения, в котором можно указать значение по умолчанию. Я скопировал и вставил ваш метод расширения, но получил сообщение об ошибке: тип 'T' должен быть типом значения, не допускающим значения NULL, чтобы использовать его в качестве параметра 'TEnum' в универсальном типе или методе 'System.Enum.TryParse
@barrypicker смотрите комментарий выше. public static T ToEnum<T>(this string value, T defaultValue) where T : struct
where T : struct, IComparable необходимо добавить в конце определения метода расширения.
Должно получиться следующее: StatusEnum myStatus; Enum.TryParse («Активный», вне myStatus);
@AltafPatel в C# 7 и выше вам не нужно сначала объявлять перечисление, вы можете сделать if (Enum.TryParse("Active", out StatusEnum myStatus)) { /* use myStatus */ }
Общее замечание по этому поводу: вы должны быть очень осторожны с помещением в него числовых строк. Они будут рассматривать нет как недопустимые значения и возвращать значение по умолчанию, как и следовало ожидать, но вместо этого будут просто анализироваться до целого числа и напрямую приводиться к типу перечисления, что, скорее всего, даст вам значение, которое не может использовать ни один из вашего кода. Однажды возникла эта проблема, когда вход поступил из файла конфигурации. Так что, если вы этого не хотите, лучше заранее явно проверить числовое содержимое.
Так что, если это применимо к каждой строке. Если ToEnum в каком-либо сценарии недостаточно ясен - вы определенно не должны быть разработчиком. Вроде да ладно ... нет ничего яснее этого. Мы пограничное программирование на английском языке.
@BojidarStanchev ToEnum достаточно ясен, проблема в том, что всегда расширяет string. Это нормально для небольшой команды, но если у вас большая команда или вы работаете над API, это может стать довольно запутанным, если каждый расширяет базовые типы .NET. Существует соглашение (за пределами .NET Core DI, которое использует это намеренно), чтобы быть осторожным при публичном расширении типов, таких как int и string, без пространства имен, поскольку пользователи вашего API получат эти расширения, они получат все расширения из каждой библиотеки. они используют.
@Nyerguds - вот почему вы должны следовать приведенному ниже ответу McKenzieG1, который рекомендует создать словарь из типа перечисления. Это позволяет избежать упаковки / распаковки и отражения, используемых в Enum.Parse и Enum.TryParse. В качестве дополнительного бонуса вы получаете производительность 0 (1) и получаете только фактические значения перечисления в типе.
@sanmcp Я сказал, что это из файла конфигурации. Файлы конфигурации обычно читаются только один раз при запуске, и этот конкретный файл конфигурации принадлежал службе Windows, поэтому его никогда не следует перезапускать, если что-то специально не требует обновления. Так что это ситуация, когда производительность не имеет ни малейшего значения.
Я сказал, что производительность - это дополнительный бонус, но важно то, что вам не нужно использовать check for numeric content in advance перед преобразованием строки в enum. Если перечисление преобразовано в словарь, как предлагает @ McKenzieG1, вам не нужно беспокоиться о том, является ли аргумент числом или не частью перечисления.
@sanmcp Да, если производительность критична, стоит потратить накладные расходы на создание словаря из отражения в первый раз, а затем на его проверку. Фактически, именно так ToEnum, который моя организация использовала во время этого ответа, полностью работал, хотя и немного по-другому - мы добавили бы ConcurrentDictionary<string, T> для каждого типа перечисления при первом синтаксическом анализе, а затем использовали бы его в качестве кеша (у нас было несколько значений которые часто возникали из большого набора).
@sanmcp Однако это является микрооптимизация, так что это не то, что в подавляющем большинстве контекстов необходимость. В основном вы анализируете перечисления при сериализации: DB читает или анализирует JSON / XML, и во всех этих случаях экономия на пропуске отражения крошечная по сравнению с синтаксическим анализом DB / JSON / XML. Просто задержка подключения к БД или сети для JSON / XML, вероятно, будет на порядок медленнее. Итак, имея это в виду, я бы порекомендовал начало с простым Enum.TryParse, а затем переключиться на какой-то кеш отражения во время оптимизации, если он вам нужен.
«Если производительность важна» (что всегда так) »- одно из самых глупых утверждений, которые я когда-либо читал на SO. Другие приводили контрпримеры, поэтому я не буду беспокоиться, но помните, что« Преждевременная оптимизация - это корень всего. зло"
Enum.Parse ваш друг:
StatusEnum MyStatus = (StatusEnum)Enum.Parse(typeof(StatusEnum), "Active");
object Enum.Parse(System.Type enumType, string value, bool ignoreCase);
Итак, если бы у вас было перечисление с именем mood, оно выглядело бы так:
enum Mood
{
Angry,
Happy,
Sad
}
// ...
Mood m = (Mood) Enum.Parse(typeof(Mood), "Happy", true);
Console.WriteLine("My mood is: {0}", m.ToString());// str.ToEnum<EnumType>()
T static ToEnum<T>(this string str)
{
return (T) Enum.Parse(typeof(T), str);
}
Обратите внимание, что производительность Enum.Parse() ужасна, потому что она реализована через отражение. (То же самое и с Enum.ToString, но в другом случае.)
Если вам нужно преобразовать строки в Enums в коде, чувствительном к производительности, лучше всего создать Dictionary<String,YourEnum> при запуске и использовать его для преобразования.
Я измерил 3 мс, чтобы преобразовать строку в Enum при первом запуске на настольном компьютере. (Просто чтобы проиллюстрировать уровень ужасности).
Вау 3 мс - это ужасные порядки
можете ли вы добавить пример кода вокруг этого, чтобы мы получили представление о том, как заменить и использовать
Если ваше приложение используют 1 миллион человек => это добавляет к 50 часам человеческой жизни, которые вы потребляете :) На использование одной страницы. :П
Хотя первый запуск 3 мс определенно ужасен, будет ли второй запуск лучше? Если это 3 мс каждый раз, мы бы избегали этого, как чумы
Мы не могли предположить, что вводимые данные являются абсолютно правильными, и выбрали вариант ответа @Keith:
public static TEnum ParseEnum<TEnum>(string value) where TEnum : struct
{
TEnum tmp;
if (!Enum.TryParse<TEnum>(value, true, out tmp))
{
tmp = new TEnum();
}
return tmp;
}
Разбирает строку в TEnum без try / catch и без метода TryParse () из .NET 4.5
/// <summary>
/// Parses string to TEnum without try/catch and .NET 4.5 TryParse()
/// </summary>
public static bool TryParseToEnum<TEnum>(string probablyEnumAsString_, out TEnum enumValue_) where TEnum : struct
{
enumValue_ = (TEnum)Enum.GetValues(typeof(TEnum)).GetValue(0);
if (!Enum.IsDefined(typeof(TEnum), probablyEnumAsString_))
return false;
enumValue_ = (TEnum) Enum.Parse(typeof(TEnum), probablyEnumAsString_);
return true;
}
Нужно ли делать описание, если код уже содержит описание? Хорошо, я сделал это :)
Используйте Enum.TryParse<T>(String, T) (≥ .NET 4.0):
StatusEnum myStatus;
Enum.TryParse("Active", out myStatus);
Его можно еще больше упростить с помощью C# 7.0 встраивание типа параметра:
Enum.TryParse("Active", out StatusEnum myStatus);
Добавьте средний логический параметр для чувствительности к регистру, и это, безусловно, самое безопасное и элегантное решение.
Давай, сколько из вас реализовали этот выбранный ответ из 2008 года, чтобы только прокрутить вниз и найти, что это лучший (современный) ответ.
@TEK Я предпочитаю ответ 2008 года.
Я не понимаю. Parse выдает объяснительные исключения для того, что пошло не так с преобразованием (значение было null, пусто или не было соответствующей константы перечисления), что намного лучше, чем логическое возвращаемое значение TryParse (которое подавляет конкретную ошибку)
Enum.TryParsevar result = Enum.TryParse<System.DayOfWeek>("55", out var parsedEnum);
@MassDotNet: Да, но var result = (System.DayOfWeek)Enum.Parse("55") и var result = (System.DayOfWeek)55 тоже. Вы не можете доверять перечислениям, чтобы быть действительными.
@Eclipse: Я никогда не предлагал иначе. Это фундаментальное раздражение в том, как Microsoft реализует синтаксический анализ значений в виде перечислений.
@MassDotNet В этом случае добавьте: && Enum.IsDefined(typeof(System.DayOfWeek), parsedEnum), чтобы убедиться, что проанализированное Enum действительно существует.
Теперь вы можете использовать методы расширения:
public static T ToEnum<T>(this string value, bool ignoreCase = true)
{
return (T) Enum.Parse(typeof (T), value, ignoreCase);
}
И вы можете вызвать их с помощью приведенного ниже кода (здесь FilterType - это тип перечисления):
FilterType filterType = type.ToEnum<FilterType>();
Я обновил это, чтобы принять значение как объект и преобразовать его в строку внутри этого метода. Таким образом, я могу взять значение типа int .ToEnum вместо только строк.
@SollyM Я бы сказал, что это ужасная идея, потому что тогда этот метод расширения будет применяться к типам объектов все. На мой взгляд, два метода расширения, один для строки и один для int, были бы чище и безопаснее.
@ Свиш, это правда. Единственная причина, по которой я это сделал, заключается в том, что наш код используется только для внутренних целей, и я хотел избежать написания двух расширений. И поскольку мы конвертируем в Enum только со строкой или int, в противном случае я не видел в этом проблемы.
@SollyM Internal или нет, но я все еще поддерживаю и использую свой код: PI был бы раздражен, если бы я получил ToEnum в каждом меню intellisense, и, как вы говорите, так как единственный раз, когда вы конвертируете в перечисление, это из строки или int, можете быть уверены, что вам понадобятся только эти два метода. И два метода не намного больше, чем один, особенно когда они такие маленькие и имеют служебный тип: P
Вы можете расширить принятый ответ значением по умолчанию, чтобы избежать исключений:
public static T ParseEnum<T>(string value, T defaultValue) where T : struct
{
try
{
T enumValue;
if (!Enum.TryParse(value, true, out enumValue))
{
return defaultValue;
}
return enumValue;
}
catch (Exception)
{
return defaultValue;
}
}
Тогда вы называете это так:
StatusEnum MyStatus = EnumUtil.ParseEnum("Active", StatusEnum.None);
Если значение по умолчанию не является перечислением, Enum.TryParse завершится ошибкой и выдаст исключение, которое будет перехвачено.
После многих лет использования этой функции в нашем коде во многих местах, может быть, стоит добавить информацию о том, что эта операция снижает производительность!
Мне не нравятся значения по умолчанию. Это может привести к непредсказуемым результатам.
когда это когда-нибудь вызовет исключение?
@andleer, если значение перечисления не соответствует тому же типу перечисления, что и значение по умолчанию
@Nelly Старый код здесь, но defaultValue и возвращаемый тип метода имеют тип T. Если типы различны, вы получите ошибку времени компиляции: «невозможно преобразовать из 'ConsoleApp1.Size' в 'ConsoleApp1.Color'» или каковы бы ни были ваши типы.
@andleer, мне очень жаль, что мой последний ответ был неправильным. Возможно, этот метод вызывает исключение Syste.ArgumentException в случае, если кто-то вызывает эту функцию со значением по умолчанию, которое не относится к типу enum. С C# 7.0 я не мог создать предложение where в T: Enum. Вот почему я уловил эту возможность попыткой улова.
Я использовал класс (строго типизированная версия Enum с улучшенным анализом и производительностью). Я нашел его на GitHub, и он должен работать и для .NET 3.5. У него есть некоторые накладные расходы на память, поскольку он буферизует словарь.
StatusEnum MyStatus = Enum<StatusEnum>.Parse("Active");
Сообщение в блоге: Enums - улучшенный синтаксис, улучшенная производительность и TryParse в NET 3.5.
И код: https://github.com/damieng/DamienGKit/blob/master/CSharp/DamienG.Library/System/EnumT.cs
public static T ParseEnum<T>(string value) //function declaration
{
return (T) Enum.Parse(typeof(T), value);
}
Importance imp = EnumUtil.ParseEnum<Importance>("Active"); //function call
==================== Полная программа ====================
using System;
class Program
{
enum PetType
{
None,
Cat = 1,
Dog = 2
}
static void Main()
{
// Possible user input:
string value = "Dog";
// Try to convert the string to an enum:
PetType pet = (PetType)Enum.Parse(typeof(PetType), value);
// See if the conversion succeeded:
if (pet == PetType.Dog)
{
Console.WriteLine("Equals dog.");
}
}
}
-------------
Output
Equals dog.
Мне нравится решение метода расширения ..
namespace System
{
public static class StringExtensions
{
public static bool TryParseAsEnum<T>(this string value, out T output) where T : struct
{
T result;
var isEnum = Enum.TryParse(value, out result);
output = isEnum ? result : default(T);
return isEnum;
}
}
}
Вот моя реализация с тестами.
using static Microsoft.VisualStudio.TestTools.UnitTesting.Assert;
using static System.Console;
private enum Countries
{
NorthAmerica,
Europe,
Rusia,
Brasil,
China,
Asia,
Australia
}
[TestMethod]
public void StringExtensions_On_TryParseAsEnum()
{
var countryName = "Rusia";
Countries country;
var isCountry = countryName.TryParseAsEnum(out country);
WriteLine(country);
IsTrue(isCountry);
AreEqual(Countries.Rusia, country);
countryName = "Don't exist";
isCountry = countryName.TryParseAsEnum(out country);
WriteLine(country);
IsFalse(isCountry);
AreEqual(Countries.NorthAmerica, country); // the 1rst one in the enumeration
}
ОСТЕРЕГАТЬСЯ:
enum Example
{
One = 1,
Two = 2,
Three = 3
}
Enum.(Try)Parse()принимает несколько аргументов, разделенных запятыми, и объединяет их с двоичным 'или' |. Вы не можете отключить это, и, на мой взгляд, вы почти никогда этого не захотите.
var x = Enum.Parse("One,Two"); // x is now Three
Даже если Three не был определен, x все равно получит значение int 3. Это еще хуже: Enum.Parse () может дать вам значение, которое даже не определено для перечисления!
Я бы не хотел испытать на себе последствия того, что пользователи, вольно или невольно, запускают такое поведение.
Кроме того, как упоминалось другими, производительность не идеальна для больших перечислений, а именно линейна по количеству возможных значений.
Предлагаю следующее:
public static bool TryParse<T>(string value, out T result)
where T : struct
{
var cacheKey = "Enum_" + typeof(T).FullName;
// [Use MemoryCache to retrieve or create&store a dictionary for this enum, permanently or temporarily.
// [Implementation off-topic.]
var enumDictionary = CacheHelper.GetCacheItem(cacheKey, CreateEnumDictionary<T>, EnumCacheExpiration);
return enumDictionary.TryGetValue(value.Trim(), out result);
}
private static Dictionary<string, T> CreateEnumDictionary<T>()
{
return Enum.GetValues(typeof(T))
.Cast<T>()
.ToDictionary(value => value.ToString(), value => value, StringComparer.OrdinalIgnoreCase);
}
На самом деле очень полезно знать, что Enum.(Try)Parse accepts multiple, comma-separated arguments, and combines them with binary 'or'. Означает, что вы можете установить значения перечисления как степени двойки, и у вас есть очень простой способ синтаксического анализа нескольких логических флагов, например. «UseSSL, NoRetries, Sync». На самом деле, вероятно, для этого он был разработан.
@pcdev Не уверен, что вы в курсе, но эта функция предназначена для поддержки (Атрибут Flags для перечислений) (docs.microsoft.com/en-us/dotnet/csharp/language-refer ence /…).
Для производительности это может помочь:
private static Dictionary<Type, Dictionary<string, object>> dicEnum = new Dictionary<Type, Dictionary<string, object>>();
public static T ToEnum<T>(this string value, T defaultValue)
{
var t = typeof(T);
Dictionary<string, object> dic;
if (!dicEnum.ContainsKey(t))
{
dic = new Dictionary<string, object>();
dicEnum.Add(t, dic);
foreach (var en in Enum.GetValues(t))
dic.Add(en.ToString(), en);
}
else
dic = dicEnum[t];
if (!dic.ContainsKey(value))
return defaultValue;
else
return (T)dic[value];
}
Вы также должны были предоставить результаты тестирования производительности, например, время, затраченное на выполнение вышеуказанного кода при преобразовании строки в перечисление с использованием вашего метода и с использованием обычного Enum.Parse, если кто-то хочет проверить строку для перечисления или перечисление в строку в C#, проверьте qawithexperts.com/article/c-sharp/…
Я обнаружил, что здесь случай со значениями перечисления, имеющими значение EnumMember, не рассматривался. Итак, начнем:
using System.Runtime.Serialization;
public static TEnum ToEnum<TEnum>(this string value, TEnum defaultValue) where TEnum : struct
{
if (string.IsNullOrEmpty(value))
{
return defaultValue;
}
TEnum result;
var enumType = typeof(TEnum);
foreach (var enumName in Enum.GetNames(enumType))
{
var fieldInfo = enumType.GetField(enumName);
var enumMemberAttribute = ((EnumMemberAttribute[]) fieldInfo.GetCustomAttributes(typeof(EnumMemberAttribute), true)).FirstOrDefault();
if (enumMemberAttribute?.Value == value)
{
return Enum.TryParse(enumName, true, out result) ? result : defaultValue;
}
}
return Enum.TryParse(value, true, out result) ? result : defaultValue;
}
И пример этого перечисления:
public enum OracleInstanceStatus
{
Unknown = -1,
Started = 1,
Mounted = 2,
Open = 3,
[EnumMember(Value = "OPEN MIGRATE")]
OpenMigrate = 4
}
Супер простой код с использованием TryParse:
var value = "Active";
StatusEnum status;
if (!Enum.TryParse<StatusEnum>(value, out status))
status = StatusEnum.Unknown;
Вы должны использовать Enum.Parse, чтобы получить значение объекта из Enum, после чего вам нужно изменить значение объекта на конкретное значение перечисления. Преобразование в значение перечисления можно выполнить с помощью Convert.ChangeType. Пожалуйста, взгляните на следующий фрагмент кода
public T ConvertStringValueToEnum<T>(string valueToParse){
return Convert.ChangeType(Enum.Parse(typeof(T), valueToParse, true), typeof(T));
}
Попробуйте этот образец:
public static T GetEnum<T>(string model)
{
var newModel = GetStringForEnum(model);
if (!Enum.IsDefined(typeof(T), newModel))
{
return (T)Enum.Parse(typeof(T), "None", true);
}
return (T)Enum.Parse(typeof(T), newModel.Result, true);
}
private static Task<string> GetStringForEnum(string model)
{
return Task.Run(() =>
{
Regex rgx = new Regex("[^a-zA-Z0-9 -]");
var nonAlphanumericData = rgx.Matches(model);
if (nonAlphanumericData.Count < 1)
{
return model;
}
foreach (var item in nonAlphanumericData)
{
model = model.Replace((string)item, "");
}
return model;
});
}
В этом примере вы можете отправить каждую строку и установить свой Enum. Если на вашем Enum были данные, которые вам нужны, верните их как свой тип Enum.
Вы перезаписываете newModel в каждой строке, поэтому, если он содержит тире, он не будет заменен. Кроме того, вам не нужно проверять, содержит ли строка что-либо, вы все равно можете просто вызвать Replace: var newModel = model.Replace("-", "").Replace(" ", "");
@LarsKristensen Да, мы можем создать метод, удаляющий не буквенно-цифровые символы.
<Extension()>
Public Function ToEnum(Of TEnum)(ByVal value As String, ByVal defaultValue As TEnum) As TEnum
If String.IsNullOrEmpty(value) Then
Return defaultValue
End If
Return [Enum].Parse(GetType(TEnum), value, True)
End Function
public TEnum ToEnum<TEnum>(this string value, TEnum defaultValue){
if (string.IsNullOrEmpty(value))
return defaultValue;
return Enum.Parse(typeof(TEnum), value, true);}
Не уверен, когда это было добавлено, но в классе Enum теперь есть
Parse<TEnum>(stringValue)
Используется так с рассматриваемым примером:
var MyStatus = Enum.Parse<StatusEnum >("Active")
или игнорирование корпуса:
var MyStatus = Enum.Parse<StatusEnum >("active", true)
Вот декомпилированные методы, которые он использует:
[NullableContext(0)]
public static TEnum Parse<TEnum>([Nullable(1)] string value) where TEnum : struct
{
return Enum.Parse<TEnum>(value, false);
}
[NullableContext(0)]
public static TEnum Parse<TEnum>([Nullable(1)] string value, bool ignoreCase) where TEnum : struct
{
TEnum result;
Enum.TryParse<TEnum>(value, ignoreCase, true, out result);
return result;
}
Это было добавлено в .NET Core 2.0 (я немного писал об этом в другой ответ)
Если имя свойства отличается от того, что вы хотите назвать (то есть языковые различия), вы можете сделать следующее:
MyType.cs
using System;
using System.Runtime.Serialization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
[JsonConverter(typeof(StringEnumConverter))]
public enum MyType
{
[EnumMember(Value = "person")]
Person,
[EnumMember(Value = "annan_deltagare")]
OtherPerson,
[EnumMember(Value = "regel")]
Rule,
}
EnumExtensions.cs
using System;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
public static class EnumExtensions
{
public static TEnum ToEnum<TEnum>(this string value) where TEnum : Enum
{
var jsonString = $"'{value.ToLower()}'";
return JsonConvert.DeserializeObject<TEnum>(jsonString, new StringEnumConverter());
}
public static bool EqualsTo<TEnum>(this string strA, TEnum enumB) where TEnum : Enum
{
TEnum enumA;
try
{
enumA = strA.ToEnum<TEnum>();
}
catch
{
return false;
}
return enumA.Equals(enumB);
}
}
Program.cs
public class Program
{
static public void Main(String[] args)
{
var myString = "annan_deltagare";
var myType = myString.ToEnum<MyType>();
var isEqual = myString.EqualsTo(MyType.OtherPerson);
//Output: true
}
}
Если вы хотите использовать значение по умолчанию, когда оно пустое или пустое (например, при извлечении из файла конфигурации и значение не существует), и генерировать исключение, когда строка или число не соответствуют ни одному из значений перечисления. Остерегайтесь оговорок в ответе Тимо (https://stackoverflow.com/a/34267134/2454604).
public static T ParseEnum<T>(this string s, T defaultValue, bool ignoreCase = false)
where T : struct, IComparable, IConvertible, IFormattable//If C# >=7.3: struct, System.Enum
{
if ((s?.Length ?? 0) == 0)
{
return defaultValue;
}
var valid = Enum.TryParse<T>(s, ignoreCase, out T res);
if (!valid || !Enum.IsDefined(typeof(T), res))
{
throw new InvalidOperationException(
$"'{s}' is not a valid value of enum '{typeof(T).FullName}'!");
}
return res;
}
Если производительность важна (что всегда так), chk ответ, данный Mckenzieg1 ниже: stackoverflow.com/questions/16100/…