简体   繁体   中英

Invert enum flags

Lets say I have the following flags:

[Flags]
public enum Foo
{
    None = 0,
    Foo1 = 1,
    Foo2 = 2,
    Foo4 = 4,
    Foo8 = 8
}

Now I have a variable foo:

var foo = Foo.Foo1 | Foo.Foo4;

What I want to get is the following the inverted flags of foo . That would mean something like this:

Foo.Foo2 | Foo.Foo8

I've tried the ~ operator. But since my enum is a int32 value it inverts all 32 bits. But I actually just would need to invert the bits used by my Foo enum .

EDIT: Foo1 | Foo4 would equal the following bitmask:

00000000 00000000 00000000 00000101

If you invert that bitmask by using the ~ operator you will get the following result:

11111111 11111111 11111111 11111010

The result I would like to have would be:

00000000 00000000 00000000 00001010

As you can see. I just would like to invert the bits USED by the Foo enumeration. Not all bits of the whole 32-integer value.

What you want to do is to combine all the values of the enum and then mask it with the complement of your current value.

        Foo value = Foo.Foo4;
        Foo allValues = (Foo)0;
        foreach (var v in Enum.GetValues(typeof(Foo)))
            allValues |= (Foo)v;
        var compliment = allValues & ~(value);

Or, you could combine the values with Linq and cache them statically for performance:

    public static class FooHelper
    {
        private readonly static Foo allValues = ((Foo[])Enum.GetValues(typeof(Foo))).Aggregate((Foo)0, (all, cur) => all | cur);

        public static Foo AllValues { get { return allValues ; } }
    }

And then later:

        var foo = Foo.Foo1 | Foo.Foo4;
        var compliment = FooHelper.AllValues & ~(foo);

Update

If you want a generic method to combine all the flag values of an enum, you can do this:

var compliment = EnumHelper.GetAll<Foo>() & ~(value);

where basic data about the enum is cached in a lazy parameterized singleton instance:

/// <summary>
/// Contains generic utilities for enums, constrained for enums only.
/// </summary>
public sealed class EnumHelper : Enums<Enum>
{
    private EnumHelper()
    {
    }
}

/// <summary>
/// For use by EnumHelper, not for direct use.
/// </summary>
public abstract class Enums<TEnumBase> where TEnumBase : class, IConvertible
{
    // Generic singleton remembering basic properties about specified enums, cached for performance.
    sealed class DataSingleton<TEnum> where TEnum : struct, TEnumBase
    {
        static readonly DataSingleton<TEnum> instance = new DataSingleton<TEnum>();

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

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

        DataSingleton()
        {
            isSigned = GetIsSigned();
            allValues = GetAll();
            hasFlags = GetHasFlags();
        }

        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 static DataSingleton<TEnum> Instance { get { return instance; } }
    }

    private static void ThrowOnEnumWithoutFlags<TEnum>(DataSingleton<TEnum> data) where TEnum : struct, TEnumBase
    {
        if (!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, TEnumBase
    {
        var data = DataSingleton<TEnum>.Instance;
        ThrowOnEnumWithoutFlags<TEnum>(data);
        return data.AllValues;
    }
}

A more elegant solution would be using mask of all the fields and perform a XOR operation on the remainder. Usually you start by adding an 'All' value to your enum (this way you are less likely to forget updating it if another values are added), so in your case:

[Flags]
public enum Foo
{
    None = 0,
    Foo1 = 1,
    Foo2 = 2,
    Foo3 = 4,
    Foo4 = 8,
    All = Foo1 | Foo2 | Foo3 | Foo4 // magic starts here
}

var foo = Foo1 | Foo4;
var fooInverted = ~Foo.All ^ ~foo; // result: Foo2|Foo3

To invert individual Enum flag try this

private Foo foos; 
private static void FlagInvert(Foo foo)
    {var res = foos.HasFlag(foo) ? foos &= ~foo : foos  |= foo;}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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