tags:

views:

114

answers:

5

I have an enum with a Flags attribute.

My question is, I'd like to get an integer bitmask of all of the options without manually combining all the bits myself. I want to do this to compare to some other int field, and I want to protect in case a future developer ads more bit options to the enum.

Another thing is the bits in my enum flags will be all manually assigned, so I cannot simply get the next value and subtract 1.

+1  A: 

Have a look at my Unconstrained Melody project, which does evil things to allow nice features to be built on generic methods constrained to enums and delegates.

In this case, I think you'd want to call Flags<YourEnumType.GetUsedBits>().

If you don't mind using an extra (very small) library, I'd like to think that Unconstrained Melody makes life nicer when dealing with flags. If you have any feature requests, I'd be happy to have a look :)

Jon Skeet
Very interesting project, thanks for the insight.
Winforms
I'll accept this answer unless someone comes up with something natively.
Winforms
I think reflection would work here too
Winforms
@Winforms: Oh you can do everything I do in Unconstrained within your own code, of course - except for use generic constraints to constrain the type argument to be an enum (or delegate). That's the evil hackery bit...
Jon Skeet
+3  A: 
// uses a ulong to handle all possible underlying types without error
var allFlags = Enum.GetValues(typeof(YourEnumType))
                   .Cast<YourEnumType>()
                   .Aggregate((YourEnumType)0, (a, x) => a | x, a => (ulong)a);
LukeH
I can't not +1 this, since it's so close to what I was typing when the "answers added" message appeared. It does fail on long enums, so maybe change the Cast to Cast<YourEnumType>?
Jon Hanna
@Jon: Fixed. The aggregate value is now typed as a `ulong` so that it can handle all possible underlying types. (I've cast it rather than leaving it as `YourEnumType` because the OP asks for an *integer* bitmask.)
LukeH
A: 

Here's a way to do it, using ideas from the generic operators written by Jon Skeet and Marc Gravell :

void Main()
{
    Console.WriteLine(CombineAllFlags<MyEnum>()); // Prints "Foo, Bar, Baz"
}

[Flags]
public enum MyEnum
{
    Foo = 1,
    Bar = 2,
    Baz = 4
}

public static TEnum CombineAllFlags<TEnum>()
{
    TEnum[] values = (TEnum[])Enum.GetValues(typeof(TEnum));
    TEnum tmp = default(TEnum);
    foreach (TEnum v in values)
    {
        tmp = EnumHelper<TEnum>.Or(tmp, v);
    }
    return tmp;
}

static class EnumHelper<T>
{
    private static Func<T, T, T> _orOperator = MakeOrOperator();

    private static Func<T, T, T> MakeOrOperator()
    {
        Type underlyingType = Enum.GetUnderlyingType(typeof(T));
        ParameterExpression xParam = Expression.Parameter(typeof(T), "x");
        ParameterExpression yParam = Expression.Parameter(typeof(T), "y");
        var expr =
            Expression.Lambda<Func<T, T, T>>(
                Expression.Convert(
                    Expression.Or(
                        Expression.Convert(xParam, underlyingType),
                        Expression.Convert(yParam, underlyingType)),
                    typeof(T)),
                xParam,
                yParam);
        return expr.Compile();
    }

    public static T Or(T x, T y)
    {
        return _orOperator(x, y);
    }   
}

This code dynamically creates a delegate that combines enum values with the OR operator

Thomas Levesque
+2  A: 

If I understood what you asked correctly this should work for you:

Enum.GetValues(typeof(Enum)).Cast<int>().Sum();
spinon
+1 from me, very clean.
Wil P
Only works if there's no duplication within the defined types, which commonly happens with synonyms or precomposed common combinations.
Jon Hanna
This is definitely the cleanest solution, also you can just get the returned Array and do any other calucation you want like ORing instead of summing.
Winforms
Nice, but it only works if the underlying type is int (otherwise the cast fails). The solution I suggested is more generic (but perhaps overkill for this case...)
Thomas Levesque
@Jon Hanna, good point. Actually, LukeH's solution is almost the same, but he combines the values with `|`, which avoids that issue.
Thomas Levesque
Well the OP did mention that these are all created manually so I don't see why there would be duplicates. As for the cast it can be set to whatever the underlying type is. Though it sounded like it was some numeric form so I just picked int as an example.
spinon
@spinon: Why wouldn't there be duplicates just because they're created manually? For example, patterns like `enum Characters { Alpha = 1, Numeric = 2, AlphaNumeric = 3 }` are commonplace.
LukeH
@LukeH I agree that those are created often. But the OP wouldn't ask about summing up all enum values then. He would know that there were duplicates in there with combined flags and would, hopefully, mention that some of the values shouldn't be included in the sum. That is my thinking. You don't ask to add all values if you only want to add certain values.
spinon
I just think these are all valid issues but not in the realm of the question the OP asked. If he had mentioned some of these scenarios I think the down votes and comments would be more relevant. But he asked a specific question and I provided a specific answer. Didn't think we need to make the answers harder than the question.
spinon
That is correct each bit value is distinct and non overlapping so sum and OR does the same thing. In the third comment on this post though I mentioned that this solution can be slightly modified if there wre duplicates but in my case there is not.
Winforms
The accept goes to spinon by the way because of Enum.GetValues(typeof(ENum)) in general.
Winforms
+2  A: 

Kind of crude but something like this?

    [Flags]
    enum SomeFlags
    {
        Flag1 = 1,
        Flag2 = 2,
        Flag3 = 4,
        Flag4 = 16,
        Flag5 = 32,
        Flag6 = 64
    }

    static void Main(string[] args)
    {
        SomeFlags flags = 0;

        SomeFlags[] values = (SomeFlags[])Enum.GetValues(typeof(SomeFlags));
        Array.ForEach<SomeFlags>(values, delegate(SomeFlags v) { flags |= v; });

        int bitMask = Convert.ToInt32(flags);

    }
Wil P