简体   繁体   中英

Neatest way to 'OR' all values in a Flagged Enum?

Given the enum :

[Flags]
public enum mytest
{
    a = 1,
    b = 2,
    c = 4
}

I've come up with two ways to represent all values in a single variable:

    var OR1 = (mytest)Enum.GetNames(typeof(mytest)).Sum(a => (int)Enum.Parse(typeof(mytest), a));
    var OR2 = (mytest)(typeof(mytest).GetEnumValues() as mytest[]).Sum(a => (int)a);

Now, although they both work, is there a neater way? Possibly a .NET method I'm missing?

Edit: For clarification, I need the function to be dynamic - I don't want to calculate it by specifying every single enum value.

If it makes sense to have an All member, just provide it directly:

[Flags]
public enum mytest
{
    a = 1,
    b = 2,
    c = 4,
    All = 7
}

Though, a more idiomatic way to write these could be:

[Flags]
public enum MyTest
{
    A = 1,
    B = 1 << 0x01,
    C = 1 << 0x02,
    All = A | B | C
}

This shows the logical progression of the enum values, and in the All case, makes it easy to add another member.

Use Enumerable.Aggregate() to bitwise-or them together. This will work even if you have enum values that represent multiple set bits, as opposed to Sum() .

var myTestValues = (MyTest[]) typeof(MyTest).GetEnumValues();
var sum = myTestValues.Aggregate((a, b) => a | b);
sum.Dump();

It's a little tricky to make this generic because you can't constrain generic types to be enums, nor do the primitive types have any subtype relationship to one another. The best I could come up with assumes that the underlying type is int which should be good enough most of the time:

TEnum AllEnums<TEnum>() 
{
    var values = typeof(TEnum).GetEnumValues().Cast<int>();
    return (TEnum) (object) values.Aggregate((a,b) => a|b);
}

For a generic method, use Linq's Enumerable.Aggregate extension method;

var flags = Enum.GetValues(typeof(mytest))
                .Cast<int>()
                .Aggregate(0, (s, f) => s | f);

Or in a wrapper method

TEnum GetAll<TEnum>() where TEnum : struct
{
    return (TEnum) (object)
            Enum.GetValues(typeof(TEnum))
                .Cast<int>()
                .Aggregate(0, (s, f) => s | f);
}

full credit for this double-cast trick goes to @millimoose

The easiest way to ensure that all of the enum's bits are set it to just set all bits:

mytest allValues = (mytest)int.MaxValue;

This assumes that there's no problem setting bits that don't correspond to any enum, but that's likely true. You can AND this with any enum value and it will come out true, which is most likely the end goal.

How about something like

var all = Enum.GetValues(typeof(MyEnum)).Cast<MyEnum>().Last() * 2 - 1;

basically

all = max*2-1

this only works if all values are present from 1 to the max value.

1,2,4...32,64...

It is not as easy as it looks at first sight given the underlying type cast issues:

static public TEnum GetAllFlags<TEnum>() where TEnum : struct, IComparable, IFormattable, IConvertible
    {
        unchecked
        {
            if (!typeof(TEnum).IsEnum)
                throw new InvalidOperationException("Can't get flags from non Enum");
            object val = null;
            switch (Type.GetTypeCode(Enum.GetUnderlyingType(typeof(TEnum))))
            {
                case TypeCode.Byte:
                case TypeCode.SByte:
                    val = Enum.GetValues(typeof(TEnum))
                                .Cast<Byte>()
                                .Aggregate(default(Byte), ( s, f) => (byte)(s | f));
                    break;
                case TypeCode.Int16:
                case TypeCode.UInt16:
                    val = Enum.GetValues(typeof(TEnum))
                                .Cast<UInt16>()
                                .Aggregate(default(UInt16), ( s, f) => (UInt16)(s | f));
                    break;
                case TypeCode.Int32:
                case TypeCode.UInt32:
                    val = Enum.GetValues(typeof(TEnum))
                                .Cast<UInt32>()
                                .Aggregate(default(UInt32), ( s, f) => (UInt32)(s | f));
                    break;
                case TypeCode.Int64:
                case TypeCode.UInt64:
                    val = Enum.GetValues(typeof(TEnum))
                                .Cast<UInt64>()
                                .Aggregate(default(UInt64), ( s, f) => (UInt64)(s | f));
                    break;
                default :
                    throw new InvalidOperationException("unhandled enum underlying type");

            }
            return (TEnum)Enum.ToObject(typeof(TEnum), val);
        }
    }

More about this kind of conversions can be found here

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