16

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.

istepaniuk
  • 4,016
  • 2
  • 32
  • 60
maxp
  • 24,209
  • 39
  • 123
  • 201

6 Answers6

21

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.

Oded
  • 489,969
  • 99
  • 883
  • 1,009
  • This is what I usually do, in the cases where I care if they are all set. (No idea why this answer would be downvoted!) – Matthew Watson Mar 08 '13 at 16:12
  • 2
    Not my downvote, but perhaps the idea of hardcoding a specific enum to represent all others isnt ideal - if I add an enum value, then I have to make two changes every time. Plus the code in the other examples wouldnt work. – maxp Mar 08 '13 at 16:13
  • 2
    It's also common to provide a None member equal to 0 when creating flag enums. – JamieSee Mar 08 '13 at 16:14
  • @maxp - In one localized type. – Oded Mar 08 '13 at 16:14
  • 1
    So you have to make two changes in one well defined place, rather than having to call a utility method whenever you want to check if all values are set? I think that's a reasonable tradeoff, meself. @JamieSee: Good point! Oded, you should add that to your answer. :) – Matthew Watson Mar 08 '13 at 16:15
  • This makes perfect sense, as a None flag does. It's less fragile (and faster) to manually add an n^2-1 that includes all flags than to have `.GetEnumValues()` sorcery around in the production code. `.GetEnumValues()` can be used to unit test the enum if feeling paranoic. – istepaniuk Mar 08 '13 at 16:16
  • 2
    `Sum` doesn't work if you have overlapping values. e.g. { 1, 2, 4, 7, 8 }. The `Or` is 15, the `Sum` is 22 – p.s.w.g Mar 08 '13 at 16:17
  • 1
    Using `Sum` isn't great. I commonly write flags enums like `{ None = 0x00, Read = 0x01, Write = 0x10, ReadWrite = Read | Write }` and now the sum doesn't give the correct value. – Eric Lippert Mar 08 '13 at 16:17
  • @Oded, millimoose's answer is correct for how to aggregate all flags without hardcoding flag values. I still agree that the "sorcery" answer isn't the best one except as a way to unit test. – JamieSee Mar 08 '13 at 16:21
  • `All = int.MaxValue` This requires the backing type of the enum to be `int`. If you use a `uint` or `long` for instance, use `uint.MaxValue` or `long.MaxValue`. There's even a `char.MaxValue` that I just learned about. – user2023861 Mar 08 '13 at 16:22
  • @istepaniuk How exactly is it *less* fragile to have to manually update an additional field anytime you add an enum field than having a method that does this correctly? (I'm not saying it's that much more bother, but it's certainly not less fragile.) And I'm not even going to touch the "faster" claim, it's a performance difference that is very unlikely to matter. – millimoose Mar 08 '13 at 16:23
  • @user2023861 That's a good hack, although you lose the debugging benefit of .NET stringifying a `Flags` enum to a list of flags that are set. – millimoose Mar 08 '13 at 16:24
  • 4
    Also, I usually specify flag values using hex as it makes it easier to see the progression than decimal. – JamieSee Mar 08 '13 at 16:25
  • @millimoose It is less fragile simply because it's less code. Agree with you with the performance thing, it's just a bonus. Why would you add an extension method if a simple constant does the job? bugs = k * LOC. – istepaniuk Mar 08 '13 at 16:27
  • 2
    Also, I would write `All = a | b | c;` rather than `All = 7;`. Adding a new enum member then is as simple as appending `| newEnumMember` to the initialisation of `All`. – Matthew Watson Mar 08 '13 at 16:30
  • @istepaniuk I wouldn't, but that's because I don't really use flags all that often, and as I said I consider the extra constant only marginally more error-prone to the point where choosing which approach to use is a toss-up. – millimoose Mar 08 '13 at 16:31
  • I use it to indicate a kind of *'Hey you might want to try this instead'* for values I expect to be used more frequently like @EricLippert 's `ReadWrite`. I only expect users to construct flags in edge cases. `None` and `All` values don't always make sense, because sometimes flags are mutually exclusive. – p.s.w.g Mar 08 '13 at 16:37
  • @p.s.w.g In those cases, it probably is better to either split them up into multiple arguments, or not use `[Flags]`. – Markus Jarderot Mar 08 '13 at 16:42
  • @MarkusJarderot Especially with named optional parameters in C# nowadays, it seems like flags arguments aren't all that generally useful. – millimoose Mar 08 '13 at 16:47
  • you can also use [this](http://stackoverflow.com/questions/387424/what-is-the-tilde-in-a-c-sharp-enumeration) neat trick with unary one's complement operator – Mariusz Pawelski Mar 14 '13 at 12:20
14

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);
}
millimoose
  • 39,073
  • 9
  • 82
  • 134
  • Why do you need `Enum.ToObject`? I'm not 100% sure, but doesn't the cast work by itself? – p.s.w.g Mar 08 '13 at 16:22
  • @p.s.w.g It doesn't, because you can't cast unrelated types (`int` to the unconstrained `TEnum`) willy-nilly. For a cast to be legal the compiler has to know there's at least a possibility of it making sense, and it knows that `int` isn't really castable to anything without an explicit conversion of some sort. (This I admit is mostly a guesstimate of the rules, but either way the compiler complains about a direct cast.) – millimoose Mar 08 '13 at 16:25
  • @p.s.w.g Scratch that, turns out you're right after all. (Even if the double-cast construct is kind of hilarious.) – millimoose Mar 08 '13 at 16:29
  • @p.s.w.g I'd actually really love to know why the compiler will accept that but not a direct cast. – millimoose Mar 08 '13 at 16:45
6

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

p.s.w.g
  • 146,324
  • 30
  • 291
  • 331
  • +1 Just put it in a generic method and you'll be able to use it for any enum. `T GetAll() { return (T)(object)Enum.GetValues(typeof(T))... }` – Adriano Repetti Mar 08 '13 at 16:34
  • unfortunately if the underlying enum type is a different size than int it with throw an invalid cast exception. I have a solution below – dmihailescu Dec 20 '13 at 23:55
1

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.

Servy
  • 202,030
  • 26
  • 332
  • 449
  • Are you certain this code works? I think i tried someonething similar a while ago that didnt. – maxp Mar 08 '13 at 16:20
  • @maxp Works fine for me in a few simple tests. Can you come up with a test case that doesn't work? – Servy Mar 08 '13 at 16:23
  • This does work. Just tested it because I thought the cast would raise an exception, but nope. It works. – istepaniuk Mar 08 '13 at 16:23
  • @istepaniuk Obviously if the enum is backed by a different numeric field, such as a short or long, you'll need to use the appropriate type's `MaxValue` method, but that'll all that would need to change. – Servy Mar 08 '13 at 16:24
  • 1
    You're creating an "invalid" enum and you can't simply compare it with ==. Moreover...it matches **any combination**...how can you use it to check if all flags are set? – Adriano Repetti Mar 08 '13 at 16:26
  • @Adriano That depends on how it's used. If you know that there is some method that will check if `a` is set, and `b` is set, and you just want to say that "use all the things" you could use this. If you want to validate if a provided value has all fields set and isn't missing any, then you are correct that this won't work. – Servy Mar 08 '13 at 16:37
  • Hmmmm I wouldn't create an invalid enum to save few lines of code so I guess he needs to check if all flags are set or not but...it's just a guess. – Adriano Repetti Mar 08 '13 at 16:41
  • does not make much sense, especially if the underlying Enum type is uint, long or ulong. – dmihailescu Dec 19 '13 at 14:54
  • @dmihailescu You would need to use the max value of whatever the underlying type is, yes, rather than using `int` for everything. – Servy Dec 19 '13 at 14:55
1

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

John Boker
  • 82,559
  • 17
  • 97
  • 130
0

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

dmihailescu
  • 1,625
  • 17
  • 15