Làm cách nào để triển khai phương thức Enum.TryParse của .NET 4 trong .NET 3.5?Thực hiện Enum.TryParse trong .NET 3.5
public static bool TryParse<TEnum>(string value, out TEnum result) where TEnum : struct
Làm cách nào để triển khai phương thức Enum.TryParse của .NET 4 trong .NET 3.5?Thực hiện Enum.TryParse trong .NET 3.5
public static bool TryParse<TEnum>(string value, out TEnum result) where TEnum : struct
Mất nhiều thời gian hơn tôi hy vọng nhận được quyền này, nhưng nó hoạt động và đã được thử nghiệm. Hy vọng điều này tiết kiệm một ai đó một thời gian!
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;
}
Nó sẽ không thể là một phương pháp tĩnh trên Enum (phương pháp khuyến nông tĩnh hoàn toàn không có ý nghĩa), nhưng nó phải làm việc
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;
}
}
tôi không thích sử dụng một try-catch
để xử lý bất kỳ sự cố chuyển đổi hoặc các sự kiện không đặc biệt khác như là một phần của quy trình bình thường của đơn đăng ký của tôi, do đó phương pháp Enum.TryParse
của riêng mình cho .NET 3.5 và trước đó sử dụng phương pháp Enum.IsDefined()
để đảm bảo rằng sẽ không có ngoại lệ do Enum.Parse() ném ra. . Bạn cũng có thể bao gồm một số kiểm tra null trên value
để ngăn chặn một ArgumentNullException
nếu giá trị là null.
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;
}
Rõ ràng phương pháp này sẽ không nằm trong lớp Enum
vì vậy bạn sẽ cần một lớp để bao gồm điều này trong đó sẽ phù hợp.
Một hạn chế là thiếu ràng buộc enum
đối với các phương pháp chung, vì vậy bạn sẽ phải xem xét cách bạn muốn xử lý các loại không chính xác. Enum.IsDefined
sẽ ném ArgumentException
nếu TEnum
không phải là enum
nhưng tùy chọn duy nhất khác là kiểm tra thời gian chạy và ném một ngoại lệ khác, vì vậy tôi thường không thêm séc bổ sung và chỉ cho phép loại kiểm tra trong các phương pháp này xử lý cho tôi. Tôi muốn xem xét việc thêm IConvertible
làm hạn chế khác, chỉ để giúp hạn chế loại hơn nữa.
Tôi thích cái này tốt hơn so với tôi phương pháp tiếp cận (bây giờ đã xóa) cho chắc chắn. –
+1 Đồng ý, nếu tôi đã dành một phút nữa để xem xét các phương pháp Enum và xem phương pháp IsDefined, điều này rất có thể sẽ xảy ra. :) –
Cảm ơn câu trả lời của bạn. Trong khi đó là một khởi đầu tốt, có một vài cân nhắc khác để làm cho nó ngang bằng với thực hiện .NET 4 (ví dụ giá trị như các cờ được phân tách bằng dấu phẩy, giá trị là số, kiểu số enum) –
Tại NLog chúng tôi cũng cần Enum.TryParse
cho Net 3.5. Chúng tôi đã thực hiện các tính năng cơ bản (chỉ phân tích cú pháp, phân biệt chữ hoa chữ thường và không nhạy cảm, không có cờ) bị ảnh hưởng bởi bài đăng này.
Việc triển khai cơ bản này được kiểm tra đơn vị cao nên có hành vi tương tự như triển khai Microsoft .Net 4.
Mã này có thể được tìm thấy tại the NLog GitHub, và cũng là unit tests are on GitHub (xUnit)
sử dụng (Tất cả các phiên bản Net) - cùng một chữ ký như Net 4,0
EnumHelpers.TryParse(value, true, out parsedValue) //case insensitive
//or
EnumHelpers.TryParse(value, out parsedValue)
Bạn có thể thay đổi các câu lệnh chuyển đổi đó thành sự phản chiếu thay vì liệt kê tất cả các loại có thể và rút ngắn mã. –
@NickTurner: hiệu suất là quan trọng đối với phương pháp này, phản ánh không tốt cho hiệu suất. –
lưu ý: 'Enum.Tryparse' trong .net 4+ sẽ không ném các ngoại lệ này. https://msdn.microsoft.com/en-us/library/dd991317%28v=vs.110%29.aspx – Julian