Как TryParse для значения Enum?
Я хочу написать функцию, которая может проверять заданное значение (переданное как строка) на возможные значения enum
. В случае совпадения он должен возвращать экземпляр enum; в противном случае он должен вернуть значение по умолчанию.
Функция может не использовать внутри try
/catch
, что исключает использование Enum.Parse
, которая генерирует исключение при предоставлении недопустимого аргумента.
Я хотел бы использовать что-то в строках функции TryParse
для реализации этого:
public static TEnum ToEnum<TEnum>(this string strEnumValue, TEnum defaultValue)
{
object enumValue;
if (!TryParse (typeof (TEnum), strEnumValue, out enumValue))
{
return defaultValue;
}
return (TEnum) enumValue;
}
Ответы
Ответ 1
Как говорили другие, вы должны реализовать свой собственный TryParse
. Саймон Mourier обеспечивает полную реализацию, которая заботится обо всем.
Если вы используете перечисления битовых полей (например, флаги), вам также нужно обработать строку, такую как "MyEnum.Val1|MyEnum.Val2"
которая представляет собой комбинацию из двух значений перечисления. Если вы просто вызываете Enum.IsDefined
с этой строкой, она вернет false, даже если Enum.Parse
правильно ее обрабатывает.
Обновить
Как упоминалось в комментариях Lisa и Christian, Enum.TryParse
теперь доступен для С# в.NET4 и выше.
Документы MSDN
Ответ 2
Enum.IsDefined выполнит все. Это может быть не так эффективно, как TryParse, вероятно, будет, но он будет работать без обработки исключений.
public static TEnum ToEnum<TEnum>(this string strEnumValue, TEnum defaultValue)
{
if (!Enum.IsDefined(typeof(TEnum), strEnumValue))
return defaultValue;
return (TEnum)Enum.Parse(typeof(TEnum), strEnumValue);
}
Стоит отметить: в .NET 4.0 был добавлен метод TryParse
.
Ответ 3
Вот пример реализации EnumTryParse
. В отличие от других распространенных реализаций, он также поддерживает перечисление, отмеченное атрибутом Flags
.
/// <summary>
/// Converts the string representation of an enum to its Enum equivalent value. A return value indicates whether the operation succeeded.
/// This method does not rely on Enum.Parse and therefore will never raise any first or second chance exception.
/// </summary>
/// <param name="type">The enum target type. May not be null.</param>
/// <param name="input">The input text. May be null.</param>
/// <param name="value">When this method returns, contains Enum equivalent value to the enum contained in input, if the conversion succeeded.</param>
/// <returns>
/// true if s was converted successfully; otherwise, false.
/// </returns>
public static bool EnumTryParse(Type type, string input, out object value)
{
if (type == null)
throw new ArgumentNullException("type");
if (!type.IsEnum)
throw new ArgumentException(null, "type");
if (input == null)
{
value = Activator.CreateInstance(type);
return false;
}
input = input.Trim();
if (input.Length == 0)
{
value = Activator.CreateInstance(type);
return false;
}
string[] names = Enum.GetNames(type);
if (names.Length == 0)
{
value = Activator.CreateInstance(type);
return false;
}
Type underlyingType = Enum.GetUnderlyingType(type);
Array values = Enum.GetValues(type);
// some enums like System.CodeDom.MemberAttributes *are* flags but are not declared with Flags...
if ((!type.IsDefined(typeof(FlagsAttribute), true)) && (input.IndexOfAny(_enumSeperators) < 0))
return EnumToObject(type, underlyingType, names, values, input, out value);
// multi value enum
string[] tokens = input.Split(_enumSeperators, StringSplitOptions.RemoveEmptyEntries);
if (tokens.Length == 0)
{
value = Activator.CreateInstance(type);
return false;
}
ulong ul = 0;
foreach (string tok in tokens)
{
string token = tok.Trim(); // NOTE: we don't consider empty tokens as errors
if (token.Length == 0)
continue;
object tokenValue;
if (!EnumToObject(type, underlyingType, names, values, token, out tokenValue))
{
value = Activator.CreateInstance(type);
return false;
}
ulong tokenUl;
switch (Convert.GetTypeCode(tokenValue))
{
case TypeCode.Int16:
case TypeCode.Int32:
case TypeCode.Int64:
case TypeCode.SByte:
tokenUl = (ulong)Convert.ToInt64(tokenValue, CultureInfo.InvariantCulture);
break;
//case TypeCode.Byte:
//case TypeCode.UInt16:
//case TypeCode.UInt32:
//case TypeCode.UInt64:
default:
tokenUl = Convert.ToUInt64(tokenValue, CultureInfo.InvariantCulture);
break;
}
ul |= tokenUl;
}
value = Enum.ToObject(type, ul);
return true;
}
private static char[] _enumSeperators = new char[] { ',', ';', '+', '|', ' ' };
private static object EnumToObject(Type underlyingType, string input)
{
if (underlyingType == typeof(int))
{
int s;
if (int.TryParse(input, out s))
return s;
}
if (underlyingType == typeof(uint))
{
uint s;
if (uint.TryParse(input, out s))
return s;
}
if (underlyingType == typeof(ulong))
{
ulong s;
if (ulong.TryParse(input, out s))
return s;
}
if (underlyingType == typeof(long))
{
long s;
if (long.TryParse(input, out s))
return s;
}
if (underlyingType == typeof(short))
{
short s;
if (short.TryParse(input, out s))
return s;
}
if (underlyingType == typeof(ushort))
{
ushort s;
if (ushort.TryParse(input, out s))
return s;
}
if (underlyingType == typeof(byte))
{
byte s;
if (byte.TryParse(input, out s))
return s;
}
if (underlyingType == typeof(sbyte))
{
sbyte s;
if (sbyte.TryParse(input, out s))
return s;
}
return null;
}
private static bool EnumToObject(Type type, Type underlyingType, string[] names, Array values, string input, out object value)
{
for (int i = 0; i < names.Length; i++)
{
if (string.Compare(names[i], input, StringComparison.OrdinalIgnoreCase) == 0)
{
value = values.GetValue(i);
return true;
}
}
if ((char.IsDigit(input[0]) || (input[0] == '-')) || (input[0] == '+'))
{
object obj = EnumToObject(underlyingType, input);
if (obj == null)
{
value = Activator.CreateInstance(type);
return false;
}
value = obj;
return true;
}
value = Activator.CreateInstance(type);
return false;
}
Ответ 4
В конце вы должны реализовать это вокруг Enum.GetNames
:
public bool TryParseEnum<T>(string str, bool caseSensitive, out T value) where T : struct {
// Can't make this a type constraint...
if (!typeof(T).IsEnum) {
throw new ArgumentException("Type parameter must be an enum");
}
var names = Enum.GetNames(typeof(T));
value = (Enum.GetValues(typeof(T)) as T[])[0]; // For want of a better default
foreach (var name in names) {
if (String.Equals(name, str, caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase)) {
value = (T)Enum.Parse(typeof(T), name);
return true;
}
}
return false;
}
Дополнительные примечания:
-
Enum.TryParse
включен в .NET 4. См. здесь http://msdn.microsoft.com/library/dd991876(VS.100).aspx
- Другим подходом было бы непосредственно обернуть
Enum.Parse
, чтобы уловить исключение, возникшее при его завершении. Это может быть быстрее, когда совпадение найдено, но, скорее всего, будет медленнее, если нет. В зависимости от данных, которые вы обрабатываете, это может быть или не быть чистым улучшением.
РЕДАКТИРОВАТЬ: только что увидели более эффективную реализацию, которая кэширует необходимую информацию: http://damieng.com/blog/2010/10/17/enums-better-syntax-improved-performance-and-tryparse-in-net-3-5
Ответ 5
На основе .NET 4.5
Пример кода ниже
using System;
enum Importance
{
None,
Low,
Medium,
Critical
}
class Program
{
static void Main()
{
// The input value.
string value = "Medium";
// An unitialized variable.
Importance importance;
// Call Enum.TryParse method.
if (Enum.TryParse(value, out importance))
{
// We now have an enum type.
Console.WriteLine(importance == Importance.Medium);
}
}
}
Ссылка: http://www.dotnetperls.com/enum-parse
Ответ 6
У меня есть оптимизированная реализация, которую вы можете использовать в UnconstrainedMelody. Фактически он просто кэширует список имен, но делает это в хорошем, строго типизированном, ограниченном образом:)
Ответ 7
В настоящее время нет из списка Enum.TryParse. Это было запрошено в Connect (Still no Enum.TryParse) и получил ответ, указывающий на возможное включение в следующую структуру после .NET 3.5. На данный момент вам придется реализовать предлагаемые обходные пути.
Ответ 8
Единственный способ избежать обработки исключений - использовать метод GetNames(), и все мы знаем, что исключения не должны использоваться для обычной логики приложения:)
Ответ 9
Является ли кеширование динамически генерируемой функции/словаря допустимым?
Поскольку вы не знаете (как представляется) знать тип перечисления раньше времени, первое выполнение может генерировать что-то, что последующие исполнения могут использовать.
Вы даже можете кэшировать результат Enum.GetNames()
Вы пытаетесь оптимизировать процессор или память? Вам действительно нужно?
Ответ 10
enum EnumStatus
{
NAO_INFORMADO = 0,
ENCONTRADO = 1,
BLOQUEADA_PELO_ENTREGADOR = 2,
DISPOSITIVO_DESABILITADO = 3,
ERRO_INTERNO = 4,
AGARDANDO = 5
}
...
if (Enum.TryParse<EnumStatus>(item.status, out status)) {
}
Ответ 11
Как уже говорили другие, если вы не используете Try & Catch, вам нужно использовать IsDefined или GetNames...
Вот несколько примеров... они в основном все одинаковы, первый - для обработки нумеруемых перечислений. Я предпочитаю второй, поскольку это расширение для строк, а не перечислений... но вы можете смешивать их по своему желанию!
- www.objectreference.net/post/Enum-TryParse-Extension-Method.aspx
- flatlinerdoa.spaces.live.com/blog/cns!17124D03A9A052B0!605.entry
- mironabramson.com/blog/post/2008/03/Another-version-for-the-missing-method-EnumTryParse.aspx
- lazyloading.blogspot.com/2008/04/enumtryparse-with-net-35-extension.html
Ответ 12
Существует не TryParse, потому что тип Enum не известен до выполнения. TryParse, который следует той же методологии, что и метод Date.TryParse, выдает неявную ошибку преобразования для параметра ByRef.
Я предлагаю сделать что-то вроде этого:
//1 line call to get value
MyEnums enumValue = (Sections)EnumValue(typeof(Sections), myEnumTextValue, MyEnums.SomeEnumDefault);
//Put this somewhere where you can reuse
public static object EnumValue(System.Type enumType, string value, object NotDefinedReplacement)
{
if (Enum.IsDefined(enumType, value)) {
return Enum.Parse(enumType, value);
} else {
return Enum.Parse(enumType, NotDefinedReplacement);
}
}
Ответ 13
Посмотрите на класс Enum (struct?). На этом есть метод Parse, но я не уверен в tryparse.
Ответ 14
Этот метод преобразует тип перечисления:
public static TEnum ToEnum<TEnum>(object EnumValue, TEnum defaultValue)
{
if (!Enum.IsDefined(typeof(TEnum), EnumValue))
{
Type enumType = Enum.GetUnderlyingType(typeof(TEnum));
if ( EnumValue.GetType() == enumType )
{
string name = Enum.GetName(typeof(HLink.ViewModels.ClaimHeaderViewModel.ClaimStatus), EnumValue);
if( name != null)
return (TEnum)Enum.Parse(typeof(TEnum), name);
return defaultValue;
}
}
return (TEnum)Enum.Parse(typeof(TEnum), EnumValue.ToString());
}
Он проверяет базовый тип и получает имя против него для разбора. Если все не удается, оно вернет значение по умолчанию.