如何为Enum值TryParse?

我想写一个函数,可以validation一个给定的值(作为string传递)与可能的enum值。 在匹配的情况下,它应该返回枚举实例; 否则,它应该返回一个默认值。

该函数可能不会在内部使用try / catch ,不包括使用Enum.Parse ,当给定一个无效参数时抛出一个exception。

我想使用一些TryParse函数来实现这个function:

 public static TEnum ToEnum<TEnum>(this string strEnumValue, TEnum defaultValue) { object enumValue; if (!TryParse (typeof (TEnum), strEnumValue, out enumValue)) { return defaultValue; } return (TEnum) enumValue; } 

正如其他人所说,你必须实现你自己的TryParse 。 Simon Mourier正在提供一个完整的实施,负责一切。

如果你正在使用位域枚举(即标志),你还必须处理一个string,如"MyEnum.Val1|MyEnum.Val2" ,它是两个枚举值的组合。 如果你只是调用这个stringEnum.IsDefined ,它将返回false,即使Enum.Parse正确处理它。

更新

正如Lisa和Christian在评论中提到的那样, Enum.TryParse现在可用于C#.NET4以上。

MSDN文档

Enum.IsDefined将完成任务。 它可能不像TryParse那样高效,但是它可以工作而不需要exception处理。

 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方法。

这是一个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; } 

最后你必须实现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捕获失败时抛出的exception。 如果find匹配,这可能会更快,但如果不匹配,可能会更慢。 根据你正在处理的数据,这可能是也可能不是净提高。

编辑:刚刚看到了一个更好的实现,这caching了必要的信息: http : //damieng.com/blog/2010/10/17/enums-better-syntax-improved-performance-and-tryparse-in-net- 3-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

我有一个可以在UnconstrainedMelody中使用的优化实现。 实际上它只是caching名称列表,但它是以一种很好的,强types的,一般的限制的方式进行的:)

目前没有开箱的Enum.TryParse。 Connect( Still no Enum.TryParse )已经被请求,并且得到了一个响应,指出可能包含在.NET 3.5之后的下一个框架中。 您现在必须实施build议的解决方法。

避免exception处理的唯一方法是使用GetNames()方法,并且我们都知道exception不应该被滥用于常见的应用程序逻辑:)

caching一个dynamic生成的函数/字典允许?

因为你没有(似乎)提前知道枚举的types,所以第一次执行可能会产生一些后续执行可以利用的东西。

你甚至可以cachingEnum.GetNames()的结果

你想优化CPU或内存吗? 你真的需要吗?

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

}

正如其他人已经说过,如果你不使用Try&Catch,你需要使用IsDefined或GetNames …这里有一些样例…他们基本上都是一样的,第一个处理可空枚举。 我更喜欢第二个,因为它是string的扩展,而不是枚举…但是你可以混合它们,只要你想!

没有TryParse,因为Enum的types直到运行时才知道。 TryParse遵循与Date.TryParse方法相同的方法,将在ByRef参数上引发隐式转换错误。

我build议做这样的事情:

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

看看Enum类(struct?)本身。 有一个parsing方法,但我不知道tryparse。

该方法将转换一种枚举types:

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

它检查底层的types,并得到它的名字parsing。 如果一切都失败,它将返回默认值。