在.NET 3.5中实现Enum.TryParse

时间:2013-02-22 04:57:04

标签: c# .net

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

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

4 个答案:

答案 0 :(得分:14)

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

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

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

答案 1 :(得分:7)

比我希望的更长时间,但它确实有效并经过测试。希望这能节省一些时间!

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

答案 2 :(得分:3)

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

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

答案 3 :(得分:1)

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)