Преобразовать строку в перечисление в С#
Какой лучший способ преобразовать строку в значение перечисления в С#?
У меня есть тег select HTML, содержащий значения перечисления. Когда страница размещена, я хочу поднять значение (которое будет в форме строки) и преобразовать его в значение перечисления.
В идеальном мире я мог бы сделать что-то вроде этого:
StatusEnum MyStatus = StatusEnum.Parse("Active");
но это недействительный код.
Ответы
Ответ 1
В .NET Core и .NET > 4 существует общий метод анализа:
Enum.TryParse("Active", out StatusEnum myStatus);
Это также включает в себя новые переменные inline out
С# 7, поэтому это происходит с помощью try-parse, преобразования в явный тип перечисления и инициализирует + заполняет переменную myStatus
.
Если у вас есть доступ к С# 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 дополнительными методами, которые применяются только в очень специфических контекстах, если только ваша команда разработчиков не очень хорошо понимает, что делают эти расширения.
Ответ 2
Используйте Enum.TryParse<T>(String, T)
(≥.NET 4.0):
StatusEnum myStatus;
Enum.TryParse("Active", out myStatus);
Это может быть еще более упрощено с помощью С# 7.0 тип параметра вложения:
Enum.TryParse("Active", out StatusEnum myStatus);
Ответ 3
Обратите внимание, что производительность Enum.Parse()
ужасна, потому что она реализована с помощью отражения. (То же самое относится и к Enum.ToString
, который идет другим путем.)
Если вам нужно преобразовать строки в Enums в чувствительном к производительности коде, лучше всего при запуске создать Dictionary<String,YourEnum>
и использовать его для ваших преобразований.
Ответ 4
Вы ищете Enum.Parse.
SomeEnum enum = (SomeEnum)Enum.Parse(typeof(SomeEnum), "EnumValue");
Ответ 5
Теперь вы можете использовать методы расширения:
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>();
Ответ 6
object Enum.Parse(System.Type enumType, string value, bool ignoreCase);
Итак, если у вас настроено имя переименования, это будет выглядеть так:
enum Mood
{
Angry,
Happy,
Sad
}
// ...
Mood m = (Mood) Enum.Parse(typeof(Mood), "Happy", true);
Console.WriteLine("My mood is: {0}", m.ToString());
Ответ 7
BEWARE:
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);
}
Ответ 8
Enum.Parse твой друг:
StatusEnum MyStatus = (StatusEnum)Enum.Parse(typeof(StatusEnum), "Active");
Ответ 9
Вы можете расширить принятый ответ значением по умолчанию, чтобы избежать исключений:
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, Enum.TryParse завершится ошибкой и выдаст исключение, которое перехватывается.
После многих лет использования этой функции в нашем коде во многих местах может быть полезно добавить информацию, что эта операция стоит производительности!
Ответ 10
Мы не могли принять абсолютно корректный ввод и пошли с этим вариантом ответа @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;
}
Ответ 11
// str.ToEnum<EnumType>()
T static ToEnum<T>(this string str)
{
return (T) Enum.Parse(typeof(T), str);
}
Ответ 12
Переводит строку в 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;
}
Ответ 13
Супер простой код с использованием TryParse:
var value = "Active";
StatusEnum status;
if (!Enum.TryParse<StatusEnum>(value, out status))
status = StatusEnum.Unknown;
Ответ 14
Мне нравится решение метода расширения.
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
}
Ответ 15
Я использовал класс (строго типизированная версия Enum с синтаксическим разбором и улучшением производительности). Я нашел его на GitHub, и он тоже должен работать для .NET 3.5. У него есть некоторые служебные данные памяти, поскольку он буферизует словарь.
StatusEnum MyStatus = Enum<StatusEnum>.Parse("Active");
Blogpost Enums - лучший синтаксис, улучшенная производительность и TryParse в NET 3.5.
И код:
https://github.com/damieng/DamienGKit/blob/master/CSharp/DamienG.Library/System/EnumT.cs
Ответ 16
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.
Ответ 17
Для производительности это может помочь:
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];
}
Ответ 18
Я обнаружил, что здесь случай с значениями перечисления, которые имеют значение 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
}
Ответ 19
Вы должны использовать Enum.Parse для получения значения объекта из Enum, после чего вам нужно изменить значение объекта на определенное значение enum. Значение Casting to enum может быть выполнено с помощью Convert.ChangeType. Пожалуйста, посмотрите следующий фрагмент кода
public T ConvertStringValueToEnum<T>(string valueToParse){
return Convert.ChangeType(Enum.Parse(typeof(T), valueToParse, true), typeof(T));
}
Ответ 20
Попробуйте этот образец:
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
.
Ответ 21
<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
Ответ 22
public TEnum ToEnum<TEnum>(this string value, TEnum defaultValue){
if (string.IsNullOrEmpty(value))
return defaultValue;
return Enum.Parse(typeof(TEnum), value, true);}