Bitwise or-ing enums using generics

It's possible to bitwise or enums. Typically this is done on Flags enums.

Eg var foo = MyEnum.ABC | MyEnum.XCN

I have tried to create a method convert an array of enums into a combined enum using generics.

Here is what I have tried:

private T CombineFlags<T>(params T[] flags) where T : struct, IConvertible
{
    return flags.Select(flag => flag).Aggregate((x, y) => x | y);
}

However, I can't apply operator '\' to T and T. Casting doesn't seem to help. struct, IConvertible seem to be the closest I can get to enums, but obviously not close enough to use the '|' operator. System.Enum also isn't very helpful.

How can I perform this operation on the generic enum? (Is it possible?)


Solution 1:

There is no way to apply a generic constraint on a type that that type be an Enum, or to apply a constraint that the type overloads the | operator, so anything that you do, by necessity, won't be able to maintain entirely static typing.

What you can do is change the enum to its underlying integer type, do the aggregation, and then cast it back. The problem is that you can't (easily) solve is dynamically determining the underlying type and performing the bitwise or on that type (again due to a lack of constraints on a type having an overload of the | operator). If you can assume that the underlying type of the enum is an int (or any smaller type) then you can do it, but if the underlying type of the enum is say a long then this code would break. There's also the fact that non-enum values can be used for T, and those types may or may not function properly when passed to this method.

private static T CombineFlags<T>(params T[] flags) where T : struct, IConvertible
{
    int n = flags.Select(flag => Convert.ToInt32(flag))
        .Aggregate((x, y) => x | y);
    return (T)(object)n;
}

Solution 2:

You can create a static helper method to generate the needed Or function for your aggregate. The function is generated on first access and cached for additional uses.

This assumes that the type passed will be an enum.

public T CombineFlags<T>(params T[] flags)
    where T : struct, IConvertible
{
    return flags.Select(flag => flag).Aggregate(EnumHelper<T>.OrFunction);
}

private class EnumHelper<T>
    where T : struct,IConvertible
{
    static readonly Type typeofT = typeof(T);
    static readonly Type underlyingType = Enum.GetUnderlyingType(typeofT);
    static readonly ParameterExpression[] parameters = 
    { 
        Expression.Parameter(typeofT), 
        Expression.Parameter(typeofT) 
    };

    static readonly Func<T, T, T> _orFunc = Expression.Lambda<Func<T, T, T>>(
        Expression.Convert(Expression.Or(
            Expression.Convert(parameters[0], underlyingType),
            Expression.Convert(parameters[1], underlyingType)
        ), typeofT), parameters).Compile();

    public static Func<T, T, T> OrFunction { get { return _orFunc; } }
}