Search code examples
c#genericsenumsconstraintsextension-methods

Adding extension methods for all enums, not just a specific kind


Edit: This post is not a duplicate, those other solutions do not work in allowing me to use the bitwise operators.

I want to do something like this:

public static class EnumExt
{
    public static enum AddFlag(this enum e, enum flag) => e |= flag;
}

So I can use it like this:

Color color = Red;
color.AddFlag(Color.Dark);

It's just a bit easier to read. And I want this single method to work for all enum values, instead of making an override for each one I plan on working with. Problem is, that code won't work because enum isn't a type like int is. I tried doing something with generics:

public static class EnumExt
{
    public static T AddFlag<T>(this T e, T flag) 
        where T : Enum      // Can't constrain to Enum
    {
        return e = e | flag;
    }
}

and this:

public static T AddFlag<T>(this T e, T flag) 
    where T : struct
{
    if (!(typeof(T).IsEnum)) return default; 

    return e |= flag;      // Operator '|' cannot be applied to T
}

Which has two problems. Because it's a struct this means this method will show up for int values. I'm using struct only because it's the closest constraint to Enum. I exit the method if it's not an Enum, but that still doesn't let the compiler know that e is an enum, even though it should always be at this point.

Also tried using where T : int and using casting, but int is an invalid constraint.

Is there anyway this can be done?

EDIT: I have tried the two answers that have been suggested as solving the problem, but they do not. The one with IEnumConstraint doesn't work because it says my enum does not inherit from it, and neither of the answer allows me to actually do return e |= flag;

// Doesn't work because C# won't allow bitwise operators on generic types 
// Because the constraint is still vague enough for non-enum values to slip through
public static T AddFlag<T>(this T e, T flag)
    where T : struct, IConvertible

// Doesn't work because enums don't inherit from IEnumConstraint
// Same as above
public static T AddFlag<T>(this T e, T flag)
    where T : struct, IEnumConstraint

My guess is that, even if these did limit to only Enum values, it is still possible for some other class to inherit from IEnumConstraint and IConvertible therefore the bitwise operators won't work because it's still not a guarantee that operation will be available for T.

It seems the only real solution is in C# 7.3, where they allow you to use System.Enum as a constraint.


Solution

  • Was able to figure it out. I have to cast from T to object then to int, use the bitwise operators on the int values, then reverse it to get the result back.

    @mjwills pointed out that C# 7.3 won't be fixing the issue of casting. All it'll fix is the constraint and I'll be able to remove the throw exception.

    public static T AddFlag<T>(this ref T e, T flag)
        where T : struct, IConvertible
    {
        if (!(typeof(T).IsEnum)) throw new ArgumentException("Value must be an enum");
    
        int added = (int)(object)e | (int)(object)flag;
    
        e = (T)(object)added;
    
        return e;
    }