如何在.NET 3.5中实现.NET 4的Enum.TryParse方法?.NET 3.5中的Enum.TryParse的实现
public static bool TryParse<TEnum>(string value, out TEnum result) where TEnum : struct
如何在.NET 3.5中实现.NET 4的Enum.TryParse方法?.NET 3.5中的Enum.TryParse的实现
public static bool TryParse<TEnum>(string value, out TEnum result) where TEnum : struct
花了超过我希望得到这个权利,但它的工作原理和经过测试。希望这能节省一些时间!
private static readonly char[] FlagDelimiter = new [] { ',' };
public static bool TryParseEnum<TEnum>(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<string, object>();
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;
}
这不会是在枚举一个静态方法(静态扩展方法不太意义),但它应该工作
public static class EnumHelpers
{
public static bool TryParse<TEnum>(string value, out TEnum result)
where TEnum : struct
{
try
{
result = (TEnum)Enum.Parse(typeof(TEnum), value);
}
catch
{
return false;
}
return true;
}
}
我不喜欢使用try-catch
处理任何转换失败或其他非常规事件作为我的应用程序正常流程的一部分,因此我自己的.NET Framework 3.5及更早版本的Enum.TryParse
方法使用Enum.IsDefined()
方法确保不会有Enum.Parse()引发的异常。 。如果值为空,您还可以在value
上包含一些空检查以防止ArgumentNullException
。
public static bool TryParse<TEnum>(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
类,所以你需要一个类来包括这将是适当的。
一个限制是对泛型方法缺少enum
约束,所以您将不得不考虑如何处理不正确的类型。 Enum.IsDefined
将抛出一个ArgumentException
如果TEnum
不是enum
但唯一的其他选择是运行时检查并抛出一个不同的异常,所以我通常不会添加额外的检查,只是让这些方法中的类型检查处理我。我会考虑增加IConvertible
作为另一个约束,只是为了更好地限制类型。
我喜欢这个比我更好方法(现在删除)肯定。 – 2013-02-22 05:26:55
+1同意,如果我再花一分钟看Enum方法并看到IsDefined方法,这很可能会发生什么。 :) – 2013-02-22 05:29:20
感谢您的回答。虽然这是一个好的开始,但还有其他一些考虑因素可以使它与.NET 4的实现保持一致(例如,值为逗号分隔标志,值为数字,枚举数字类型) – 2013-02-22 12:48:44
在NLog我们还需要Enum.TryParse
对于.NET 3.5。我们已经实现了受这篇文章影响的基本功能(只是解析,区分大小写和不敏感,没有标志)。
这个基本的实现经过了高度的单元测试,所以它的行为与Microsoft的.Net 4实现相同。
的代码可以在the NLog GitHub发现,也是unit tests are on GitHub(的xUnit)
用法(所有的.NET版本) - 相同的签名.NET 4.0
EnumHelpers.TryParse(value, true, out parsedValue) //case insensitive
//or
EnumHelpers.TryParse(value, out parsedValue)
您可以将这些开关语句更改为反射,而不是列出所有可能的类型并缩短代码。 – 2014-09-24 14:00:04
@NickTurner:性能对于这种方法很重要,反射对性能不好。 – 2014-09-26 09:36:36
注意:.net 4+中的Enum.Tryparse不会抛出这些异常。 https://msdn.microsoft.com/en-us/library/dd991317%28v=vs.110%29.aspx – Julian 2015-02-06 19:12:35