在.NET 3.5中实现Enum.TryParse

我如何在.NET 3.5中实现.NET 4的Enum.TryParse方法?

public static bool TryParse(string value, out TEnum result) where TEnum : struct 

我不喜欢使用try-catch来处理任何转换失败或其他非exception事件,作为我的应用程序正常流程的一部分,所以我自己的.NET 3.5及更早版本的Enum.TryParse方法使用了Enum.IsDefined()确保Enum.Parse()不会抛出exception的方法。 如果value为null,您还可以对value包含一些空值检查以防止ArgumentNullException

 public static bool TryParse(string value, out TEnum result) where TEnum : struct, IConvertible { var retValue = value == null ? false : Enum.IsDefined(typeof(TEnum), value); result = retValue ? (TEnum)Enum.Parse(typeof(TEnum), value) : default(TEnum); return retValue; } 

显然,这个方法不会驻留在Enum类中,所以你需要一个类来包含它,这是合适的。

一个限制是缺少对generics方法的enum约束,因此您必须考虑如何处理不正确的类型。 如果TEnum不是enum Enum.IsDefined将抛出ArgumentException ,但唯一的另一个选项是运行时检查并抛出不同的exception,所以我通常不添加额外的检查,只是让这些方法中的类型检查为我处理。 我会考虑添加IConvertible作为另一个约束,只是为了帮助更多地限制类型。

花了比我希望的更长的时间,但它的工作和测试。 希望这能节省一些时间!

  private static readonly char[] FlagDelimiter = new [] { ',' }; public static bool TryParseEnum(string value, out TEnum result) where TEnum : struct { if (string.IsNullOrEmpty(value)) { result = default(TEnum); return false; } var enumType = typeof(TEnum); if (!enumType.IsEnum) throw new ArgumentException(string.Format("Type '{0}' is not an enum", enumType.FullName)); result = default(TEnum); // Try to parse the value directly if (Enum.IsDefined(enumType, value)) { result = (TEnum)Enum.Parse(enumType, value); return true; } // Get some info on enum var enumValues = Enum.GetValues(enumType); if (enumValues.Length == 0) return false; // probably can't happen as you cant define empty enum? var enumTypeCode = Type.GetTypeCode(enumValues.GetValue(0).GetType()); // Try to parse it as a flag if (value.IndexOf(',') != -1) { if (!Attribute.IsDefined(enumType, typeof(FlagsAttribute))) return false; // value has flags but enum is not flags // todo: cache this for efficiency var enumInfo = new Dictionary(); var enumNames = Enum.GetNames(enumType); for (var i = 0; i < enumNames.Length; i++) enumInfo.Add(enumNames[i], enumValues.GetValue(i)); ulong retVal = 0; foreach(var name in value.Split(FlagDelimiter)) { var trimmedName = name.Trim(); if (!enumInfo.ContainsKey(trimmedName)) return false; // Enum has no such flag var enumValueObject = enumInfo[trimmedName]; ulong enumValueLong; switch (enumTypeCode) { case TypeCode.Byte: enumValueLong = (byte)enumValueObject; break; case TypeCode.SByte: enumValueLong = (byte)((sbyte)enumValueObject); break; case TypeCode.Int16: enumValueLong = (ushort)((short)enumValueObject); break; case TypeCode.Int32: enumValueLong = (uint)((int)enumValueObject); break; case TypeCode.Int64: enumValueLong = (ulong)((long)enumValueObject); break; case TypeCode.UInt16: enumValueLong = (ushort)enumValueObject; break; case TypeCode.UInt32: enumValueLong = (uint)enumValueObject; break; case TypeCode.UInt64: enumValueLong = (ulong)enumValueObject; break; default: return false; // should never happen } retVal |= enumValueLong; } result = (TEnum)Enum.ToObject(enumType, retVal); return true; } // the value may be a number, so parse it directly switch (enumTypeCode) { case TypeCode.SByte: sbyte sb; if (!SByte.TryParse(value, out sb)) return false; result = (TEnum)Enum.ToObject(enumType, sb); break; case TypeCode.Byte: byte b; if (!Byte.TryParse(value, out b)) return false; result = (TEnum)Enum.ToObject(enumType, b); break; case TypeCode.Int16: short i16; if (!Int16.TryParse(value, out i16)) return false; result = (TEnum)Enum.ToObject(enumType, i16); break; case TypeCode.UInt16: ushort u16; if (!UInt16.TryParse(value, out u16)) return false; result = (TEnum)Enum.ToObject(enumType, u16); break; case TypeCode.Int32: int i32; if (!Int32.TryParse(value, out i32)) return false; result = (TEnum)Enum.ToObject(enumType, i32); break; case TypeCode.UInt32: uint u32; if (!UInt32.TryParse(value, out u32)) return false; result = (TEnum)Enum.ToObject(enumType, u32); break; case TypeCode.Int64: long i64; if (!Int64.TryParse(value, out i64)) return false; result = (TEnum)Enum.ToObject(enumType, i64); break; case TypeCode.UInt64: ulong u64; if (!UInt64.TryParse(value, out u64)) return false; result = (TEnum)Enum.ToObject(enumType, u64); break; default: return false; // should never happen } return true; } 

它不会是Enum上的静态方法(静态扩展方法没有意义),但它应该有效

 public static class EnumHelpers { public static bool TryParse(string value, out TEnum result) where TEnum : struct { try { result = (TEnum)Enum.Parse(typeof(TEnum), value); } catch { return false; } return true; } } 

在NLog,我们还需要Enum.TryParse用于.Net 3.5。 我们已经实现了这篇文章影响的基本function(只是解析,区分大小写,不敏感,没有标记)。

这个基本实现经过高度unit testing,因此它具有与Microsoft的.Net 4实现相同的行为。

代码可以在NLog GitHub上找到 , unit testing也在GitHub上 (xUnit)

用法(所有.Net版本) – 与.Net 4.0相同的签名

 EnumHelpers.TryParse(value, true, out parsedValue) //case insensitive //or EnumHelpers.TryParse(value, out parsedValue)