繁体   English   中英

显示可能的标志或进行修改以适应现有标志

[英]Display Possible Flags or modify to accommodate to existing flags

我有一段代码,可以从API获取枚举标志。 当此API更改和更新时,我可能会得到枚举结构中未定义的值。

例:

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

当我说(TestEnum)7时,输出该数据时得到的字符串表示形式为“ Enum1 | Enum2 | Enum3”。 但是,如果现在API更改了,并给了我一个用(TestEnum)15设置的附加标志“ 8”,则String表示形式(尤其是在Visual Studio中)将不会给我这些标志,它只会显示“ 0xF”

有没有一种方法可以使所有正确映射的位显示出来,或者像我的示例一样,“忘记” Enum4,即第四位。 有没有办法做到这一点?

您需要做的是用所有定义的值的组合屏蔽值,然后对它进行“ 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

作为通用扩展方法很难做到这一点,因为在c#中没有通用enum约束,因此无法在针对枚举的任何通用方法中直接进行位掩码。 这是一种尝试:

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM