简体   繁体   English

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

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

I have a piece of code where I get enum flags from an API. 我有一段代码,可以从API获取枚举标志。 When this API changes and updates, I might get values that are not defined in my enum structure. 当此API更改和更新时,我可能会得到枚举结构中未定义的值。

Example: 例:

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

When I say (TestEnum)7 , I get, as a string representation when outputting said data, "Enum1|Enum2|Enum3". 当我说(TestEnum)7时,输出该数据时得到的字符串表示形式为“ Enum1 | Enum2 | Enum3”。 However, if now the API changes, and gives me an additional flag "8" that is set with (TestEnum)15 , the String representation, especially in Visual Studio, will not give me the flags, it will only Display "0xF" 但是,如果现在API更改了,并给了我一个用(TestEnum)15设置的附加标志“ 8”,则String表示形式(尤其是在Visual Studio中)将不会给我这些标志,它只会显示“ 0xF”

Is there a way to either get all the bits that are mapped properly to be displayed, or like in my example, to "forget" Enum4, the forth bit. 有没有一种方法可以使所有正确映射的位显示出来,或者像我的示例一样,“忘记” Enum4,即第四位。 Is there a way to do this? 有没有办法做到这一点?

What you have to do is mask the value with the combination of all defined values and do "ToString()" on that, and then mask the value with the compliment of all defined values and do ToString() on the remainder: 您需要做的是用所有定义的值的组合屏蔽值,然后对它进行“ 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);

And the output is 输出是

    Enum1, Enum2, Enum3, 0x00000038

Doing this as a generic extension method is weirdly hard, because there's no generic enum constraint in c#, and thus no way to do bitmasking directly in any generic method targetted at enums. 作为通用扩展方法很难做到这一点,因为在c#中没有通用enum约束,因此无法在针对枚举的任何通用方法中直接进行位掩码。 Here's one attempt: 这是一种尝试:

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