7

.NET3.5で.NET4のEnum.TryParseメソッドを実装するにはどうすればよいですか?

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

4 に答える 4

15

try-catchアプリケーションの通常のフローの一部として、変換の失敗やその他の例外ではないイベントを処理するためにを使用するのは嫌いです。そのため、 Enum.TryParse.NET 3.5以前の独自のメソッドでは、このEnum.IsDefined()メソッドを使用して、例外が発生しないようにします。 Enum.Parse()によってスローされます。if値がnull にvalueなるのを防ぐために、いくつかのnullチェックを含めることもできます。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ため、これを含めるためのクラスが必要になります。

1つの制限は、enumジェネリックメソッドに制約がないことです。そのため、誤った型をどのように処理するかを検討する必要があります。 ifはEnum.IsDefinedスローされませんが、他の唯一のオプションはランタイムチェックと別の例外のスローです。したがって、通常、追加のチェックを追加せず、これらのメソッドの型チェックで処理できるようにします。タイプをさらに制約するために、別の制約として追加することを検討します。ArgumentExceptionTEnumenumIConvertible

于 2013-02-22T05:23:22.963 に答える
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;
    }
于 2013-02-22T12:44:38.613 に答える
3

列挙型の静的メソッドではありませんが(静的拡張メソッドはあまり意味がありません)、機能するはずです

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
        {
            result = default;
            return false;
        }

        return true;
    }
}
于 2013-02-22T05:05:50.533 に答える
3

NLogでは、.Net3.5も必要でしEnum.TryParseた。この投稿の影響を受ける基本機能(解析のみ、大文字と小文字の区別、フラグなし)を実装しました。

この基本的な実装は高度な単体テストが行​​われているため、Microsoftの.Net4実装と同じ動作をします。

/// <summary>
/// Enum.TryParse implementation for .net 3.5 
/// 
/// </summary>
/// <returns></returns>
/// <remarks>Don't uses reflection</remarks>
// ReSharper disable once UnusedMember.Local
private static bool TryParseEnum_net3<TEnum>(string value, bool ignoreCase, out TEnum result) where TEnum : struct
{
    var enumType = typeof(TEnum);
    if (!enumType.IsEnum())
        throw new ArgumentException($"Type '{enumType.FullName}' is not an enum");

    if (StringHelpers.IsNullOrWhiteSpace(value))
    {
        result = default(TEnum);
        return false;
    }

    try
    {
        result = (TEnum)Enum.Parse(enumType, value, ignoreCase);
        return true;
    }
    catch (Exception)
    {
        result = default(TEnum);
        return false;
    }
}

そして使用しています:

public static class StringHelpers
{
    /// <summary>
    /// IsNullOrWhiteSpace, including for .NET 3.5
    /// </summary>
    /// <param name="value"></param>
    /// <returns></returns>
    [ContractAnnotation("value:null => true")]
    internal static bool IsNullOrWhiteSpace(string value)
    {
#if NET3_5

        if (value == null) return true;
        if (value.Length == 0) return true;
        return String.IsNullOrEmpty(value.Trim());
#else
        return string.IsNullOrWhiteSpace(value);
#endif
    }
}

コードはNLogGitHubにあり、単体テストはGitHub (xUnit)にあります。

于 2015-04-19T21:46:57.090 に答える