0

API から列挙フラグを取得するコードがあります。この API が変更および更新されると、列挙型構造で定義されていない値を取得する可能性があります。

例:

[Flags]
public enum TestEnum
{
   Enum1 = 1,
   Enum2 = 2,
   Enum3 = 4
}

と言うと(TestEnum)7、上記のデータを出力するときの文字列表現として、「Enum1 | Enum2 | Enum3」を取得します。ただし、API が変更され(TestEnum)15、 で設定された追加のフラグ「8」が表示される場合、特に Visual Studio の文字列表現ではフラグが表示されず、「0xF」のみが表示されます。

適切にマップされたすべてのビットを表示する方法、または私の例のように、4 番目のビットである Enum4 を「忘れる」方法はありますか。これを行う方法はありますか?

4

1 に答える 1

0

あなたがしなければならないことは、すべての定義された値の組み合わせで値をマスクし、それに対して "ToString()" を実行してから、すべての定義された値の補数で値をマスクし、残りに対して ToString() を実行することです:

    TestEnum value = (TestEnum)(64 - 1);
    var all = ((TestEnum[])Enum.GetValues(typeof(TestEnum))).Aggregate((TestEnum)0, (a, c) => a | c);
    var str = (value & all).ToString() + ((value & ~all) == 0 ? "" : ", 0x" + (value & ~all).ToString("X"));
    Debug.WriteLine(str);

そして、出力は

    Enum1, Enum2, Enum3, 0x00000038

これをジェネリック拡張メソッドとして行うのは非常に困難enumです。C# にはジェネリック制約がないため、列挙型を対象とするジェネリック メソッドで直接ビットマスキングを行う方法がないからです。ここに1つの試みがあります:

public static class EnumHelper
{
    // Generic singleton remembering basic properties about specified enums, cached for performance.
    sealed class DataSingleton<TEnum> where TEnum : struct, IConvertible, IComparable, IFormattable
    {
        static readonly DataSingleton<TEnum> instance = new DataSingleton<TEnum>();

        readonly bool isSigned;
        readonly TEnum allValues;
        readonly bool hasFlags;
        readonly bool isEnum;

        // Explicit static constructor to tell C# compiler
        // not to mark type as beforefieldinit
        static DataSingleton()
        {
        }

        DataSingleton()
        {
            var type = typeof(TEnum);
            isEnum = type.IsEnum;
            if (isEnum)
            {
                isSigned = GetIsSigned();
                allValues = GetAll();
                hasFlags = GetHasFlags();
            }
            else
            {
                isSigned = false;
                allValues = default(TEnum);
                hasFlags = false;
            }
        }

        static bool GetHasFlags()
        {
            var attributes = typeof(TEnum).GetCustomAttributes(typeof(FlagsAttribute), false);
            return attributes != null && attributes.Length > 0;
        }

        static bool GetIsSigned()
        {
            var underlyingType = Enum.GetUnderlyingType(typeof(TEnum));
            bool isSigned = (underlyingType == typeof(long) || underlyingType == typeof(int) || underlyingType == typeof(short) || underlyingType == typeof(sbyte));
            bool isUnsigned = (underlyingType == typeof(ulong) || underlyingType == typeof(uint) || underlyingType == typeof(ushort) || underlyingType == typeof(byte));
            if (!isSigned && !isUnsigned)
                throw new InvalidOperationException();
            return isSigned;
        }

        static TEnum GetAll()
        {
            if (GetIsSigned())
            {
                long value = 0;
                foreach (var v in Enum.GetValues(typeof(TEnum)))
                    // Not sure I need the culture but Microsoft passes it in Enum.ToUInt64(Object value) - http://referencesource.microsoft.com/#mscorlib/system/enum.cs
                    value |= Convert.ToInt64(v, CultureInfo.InvariantCulture);
                return (TEnum)Enum.ToObject(typeof(TEnum), value);
            }
            else
            {
                ulong value = 0;
                foreach (var v in Enum.GetValues(typeof(TEnum)))
                    // Not sure I need the culture but Microsoft passes it in Enum.ToUInt64(Object value) - http://referencesource.microsoft.com/#mscorlib/system/enum.cs
                    value |= Convert.ToUInt64(v, CultureInfo.InvariantCulture);
                return (TEnum)Enum.ToObject(typeof(TEnum), value);
            }
        }

        public bool HasFlags { get { return hasFlags; } }

        public bool IsSigned { get { return isSigned; } }

        public TEnum AllValues { get { return allValues; } }

        public bool IsEnum { get { return isEnum; } }

        public static DataSingleton<TEnum> Instance { get { return instance; } }
    }

    private static void ThrowOnNonEnum<TEnum>(DataSingleton<TEnum> data) where TEnum : struct, IConvertible, IComparable, IFormattable
    {
        if (!data.IsEnum)
        {
            throw (new ArgumentException("The generic argument [<TEnum>] must be an enumeration.", "TEnum: " + typeof(TEnum).FullName));
        }
    }

    private static void ThrowOnEnumWithFlags<TEnum>(DataSingleton<TEnum> data) where TEnum : struct, IConvertible, IComparable, IFormattable
    {
        if (!data.IsEnum || data.HasFlags)
        {
            throw (new ArgumentException("The generic argument [<TEnum>] must be an enumeration without the [FlagsAttribute] applied.", "TEnum: " + typeof(TEnum).FullName));
        }
    }

    private static void ThrowOnEnumWithoutFlags<TEnum>(DataSingleton<TEnum> data) where TEnum : struct, IConvertible, IComparable, IFormattable
    {
        if (!data.IsEnum || !data.HasFlags)
        {
            throw (new ArgumentException("The generic argument [<TEnum>] must be an enumeration with the [FlagsAttribute] applied.", "TEnum: " + typeof(TEnum).FullName));
        }
    }

    public static TEnum GetAll<TEnum>() where TEnum : struct, IConvertible, IComparable, IFormattable
    {
        var data = DataSingleton<TEnum>.Instance;
        ThrowOnEnumWithoutFlags<TEnum>(data);
        return data.AllValues;
    }

    static ulong ToUInt64<TEnum>(TEnum value) where TEnum : struct, IConvertible, IComparable, IFormattable
    {
        // Silently convert the value to UInt64 from the other base 
        // types for enum without throwing an exception.
        // This is need since the Convert functions do overflow checks.
        TypeCode typeCode = value.GetTypeCode();
        ulong result;

        switch (typeCode)
        {
            case TypeCode.SByte:
            case TypeCode.Int16:
            case TypeCode.Int32:
            case TypeCode.Int64:
                unchecked
                {
                    result = (UInt64)value.ToInt64(CultureInfo.InvariantCulture);
                }
                break;

            case TypeCode.Byte:
            case TypeCode.UInt16:
            case TypeCode.UInt32:
            case TypeCode.UInt64:
            case TypeCode.Boolean:
            case TypeCode.Char:
                unchecked
                {
                    result = value.ToUInt64(CultureInfo.InvariantCulture);
                }
                break;

            default:
                throw new InvalidOperationException();
        }
        return result;
    }

    public static string ToDebugString<TEnum>(this TEnum anEnum) where TEnum : struct, IConvertible, IComparable, IFormattable
    {
        var data = DataSingleton<TEnum>.Instance;
        if (!data.IsEnum || !data.HasFlags)
            return anEnum.ToString();
        var allLong = ToUInt64(data.AllValues);
        var enumLong = ToUInt64(anEnum);
        string str1 = ((TEnum)Enum.ToObject(typeof(TEnum), enumLong & allLong)).ToString(CultureInfo.InvariantCulture);
        var compliment = enumLong & ~(allLong);
        if (compliment == 0)
            return str1;
        return str1 + ", 0x" + ((TEnum)Enum.ToObject(typeof(TEnum), compliment)).ToString("X", CultureInfo.InvariantCulture);
    }
}
于 2014-08-10T21:45:11.520 に答える