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

后端 未结 6 854
伪装坚强ぢ
伪装坚强ぢ 2021-01-17 09:43

Given the enum:

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

I\'ve come up with two ways to represent all va

相关标签:
6条回答
  • 2021-01-17 10:03

    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.

    0 讨论(0)
  • 2021-01-17 10:03

    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

    0 讨论(0)
  • 2021-01-17 10:04

    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.

    0 讨论(0)
  • 2021-01-17 10:12

    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

    0 讨论(0)
  • 2021-01-17 10:20

    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);
    }
    
    0 讨论(0)
  • 2021-01-17 10:21

    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...

    0 讨论(0)
提交回复
热议问题