17

I'd like one general purpose function that could be used with any Flags style enum to see if a flag exists.

This doesn't compile, but if anyone has a suggestion, I'd appreciate it.

public static Boolean IsEnumFlagPresent<T>(T value,T lookingForFlag) 
       where T:enum
{
    Boolean result = ((value & lookingForFlag) == lookingForFlag);
    return result ;            
}
akjoshi
  • 15,374
  • 13
  • 103
  • 121
jeff
  • 3,269
  • 3
  • 28
  • 45

11 Answers11

28

No, you can't do this with C# generics. However, you could do:

public static bool IsEnumFlagPresent<T>(T value, T lookingForFlag) 
    where T : struct
{
    int intValue = (int) (object) value;
    int intLookingForFlag = (int) (object) lookingForFlag;
    return ((intValue & intLookingForFlag) == intLookingForFlag);
}

This will only work for enums which have an underlying type of int, and it's somewhat inefficient because it boxes the value... but it should work.

You may want to add an execution type check that T is actually an enum type (e.g. typeof(T).BaseType == typeof(Enum))

Here's a complete program demonstrating it working:

using System;

[Flags]
enum Foo
{
    A = 1,
    B = 2,
    C = 4,
    D = 8
}

class Test
{
    public static Boolean IsEnumFlagPresent<T>(T value, T lookingForFlag) 
        where T : struct
    {
        int intValue = (int) (object) value;
        int intLookingForFlag = (int) (object) lookingForFlag;
        return ((intValue & intLookingForFlag) == intLookingForFlag);
    }

    static void Main()
    {
        Console.WriteLine(IsEnumFlagPresent(Foo.B | Foo.C, Foo.A));
        Console.WriteLine(IsEnumFlagPresent(Foo.B | Foo.C, Foo.B));
        Console.WriteLine(IsEnumFlagPresent(Foo.B | Foo.C, Foo.C));
        Console.WriteLine(IsEnumFlagPresent(Foo.B | Foo.C, Foo.D));
    }
}
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    This would let you pass something that's not an Enum in, though, and not be very safe. If you're going to box, why not just pass in the parameter as System.Enum instead of using generics? – Reed Copsey Jun 12 '09 at 16:39
  • 1
    True, that would do it too... It would allow you to pass in "null" for both values, admittedly, so you'd still need an execution-time check... – Jon Skeet Jun 12 '09 at 17:00
  • Another benefit of making it generic is that this checks that both values are the *same* type. You can't pass in ErrorCode.Foo, HttpStatusCode.Bar. – Jon Skeet Jun 12 '09 at 17:01
  • This raises the question why .NET prohibits certain types from being used in generic constraints - System.Delegate, System.Enum, atomic types like int, long, etc. I haven't seen a compelling technical reason why this can't be supported. – LBushkin Jun 12 '09 at 17:54
  • Too many desirable features, not enough time. IIRC, Eric Lippert has expressed regret on the lack of this feature before as well. (Cue Eric to confirm/deny.) – Jon Skeet Jun 12 '09 at 18:12
24

You're looking to replace one line of code with a function that wraps one line of code? I'd say to just use the one line of code...

Paul Sonier
  • 38,903
  • 3
  • 77
  • 117
  • 3
    Holy cow, I've got more upvotes than the Skeet. This can't possibly last... :-) – Paul Sonier Jun 12 '09 at 16:29
  • 7
    It's a good answer - and I didn't even *think* of replying with that. I went straight into the implementation side of things. Note to self: must take a step back... – Jon Skeet Jun 12 '09 at 16:30
  • 1
    It's worth pointing out that the need to do the != 0 == 0 check depending on the desire is somewhat painful for people not used to the c/c++ kind of style can be a bit annoying. The point still holds though so +1 – ShuggyCoUk Jun 12 '09 at 16:47
  • 1
    Although I can appreciate the irony in replacing one line of code with a one line function, it's not entirely misguided. If this is used often it's better to have that functionality in one place in case he needs to add more to it. – Ron Warholic Jun 12 '09 at 17:23
  • What fun. Now imagine there are many FlagStyle enums and you are working with a bunch of newbs that have no clue what a FlagStyle enum is, much less understanding bitwise operations. I am worried enough about them getting FlagStyleEnumVar = FlagStyleEnumVar | lagStyleEnumVar.SomeThingNew. – jeff Jun 12 '09 at 17:32
16

For what its worth, I recently read that this feature will be part of .NET 4.0. Specifically, it is implemented in the Enum.HasFlag() function.

Steve Guidi
  • 19,700
  • 9
  • 74
  • 90
6

I have used this before:

public static bool In<T>(this T me, T values)
    where T : struct, IConvertible
{
    return (me.ToInt64(null) & values.ToInt64(null)) > 0;
}

What I like about it is you can use this clean syntax to call it since in 3.5 the compiler will can infer generic parameters.

AttributeTargets a = AttributeTargets.Class;
if (a.In(AttributeTargets.Class | AttributeTargets.Module))
{
   // ...
}
Kleinux
  • 1,511
  • 10
  • 22
2

You can do this without generics:

static bool ContainsFlags(Enum value, Enum flag)
{
    if (Enum.GetUnderlyingType(value.GetType()) == typeof(ulong))
        return (Convert.ToUInt64(value) & Convert.ToUInt64(flag)) == Convert.ToUInt64(flag);
    else
        return (Convert.ToInt64(value) & Convert.ToInt64(flag)) == Convert.ToInt64(flag);
}

I'm converting to Int64 in this case, which should handle every case except ulong, which is why the extra check...

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
2

Why not write an extension method for this? I did this in another post

public static class EnumerationExtensions {

    public static bool Has<T>(this System.Enum type, T value) {
        try {
            return (((int)(object)type & (int)(object)value) == (int)(object)value);
        } 
        catch {
            return false;
        }
    }
    //... etc...

}

//Then use it like this
bool hasValue = permissions.Has(PermissionTypes.Delete);

It could use a little refinement (since it assumes everything can be cast as an int), but it could get you started...

Community
  • 1
  • 1
hugoware
  • 35,731
  • 24
  • 60
  • 70
1

Question long over, but here's one for reference anyway:

    public static bool HasFlag<TEnum>(this TEnum enumeratedType, TEnum value)
        where TEnum : struct, IComparable, IFormattable, IConvertible

    {
        if (!(enumeratedType is Enum))
        {
            throw new InvalidOperationException("Struct is not an Enum.");
        }

        if (typeof(TEnum).GetCustomAttributes(
            typeof(FlagsAttribute), false).Length == 0)
        {
            throw new InvalidOperationException("Enum must use [Flags].");
        }

        long enumValue = enumeratedType.ToInt64(CultureInfo.InvariantCulture);
        long flagValue = value.ToInt64(CultureInfo.InvariantCulture);

        if ((enumValue & flagValue) == flagValue)
        {
            return true;
        }

        return false;
    }
x0n
  • 51,312
  • 7
  • 89
  • 111
  • 2
    This code depends on the Flags Enum being of Type Long and the IConvertible constraint. You can use the Enum constraint in .C# 7.3 and later and make this work with other types of Enum value by using: Convert.ToUInt64((object) somenumvaluevalue) – BillW Jul 06 '21 at 14:48
  • 2
    Sound advice @BillW nearly 12 years later :) – x0n Jul 22 '21 at 21:50
1

Today, you can set the c# language version to >=7.3 and use the next code as the reference:

public static class EnumExt
{
    public static IEnumerable<TEnum> Explode<TEnum>(this TEnum enumValue) where TEnum : Enum
    {
        var res = Enum.GetValues(enumValue.GetType())
            .Cast<TEnum>()
            .Where(x => enumValue.HasFlag(x));
        return res;
    }

    public static string ExplodeToString<TEnum>(this TEnum enumValue, string delimeter = ",") where TEnum : Enum
    {
        return string.Join(delimeter, Explode(enumValue));
    }
}
Dmytro Bondarenko
  • 865
  • 1
  • 7
  • 12
  • If you create a Flags enum with the value `None` you're going to automatically add it every time since HasFlag will return true – Lorenzo Mar 24 '21 at 03:39
1

Worth pointing out that simply providing some static overloads for all the integral types will work so long as you know you are working with a specific enum. They won't work if the consuming code is likewise operating on where t : struct

If you need to deal with arbitrary (struct) T

You cannot currently do a fast conversion of a generically typed struct into some alternate bitwise form (i.e. roughly speaking a reinterpret_cast) without using C++/CLI

generic <typename T>
where T : value class
public ref struct Reinterpret
{
    private:
    const static int size = sizeof(T);

    public:    
    static int AsInt(T t)
    {
        return *((Int32*) (void*) (&t));
    }
}

This will then let you write:

static void IsSet<T>(T value, T flags) where T : struct
{
    if (!typeof(T).IsEnum)
        throw new InvalidOperationException(typeof(T).Name +" is not an enum!");
    Type t = Enum.GetUnderlyingType(typeof(T));
    if (t == typeof(int))
    {
         return (Reinterpret.AsInt(value) & Reinterpret.AsInt(flags)) != 0
    }
    else if (t == typeof(byte))
    {
         return (Reinterpret.AsByte(value) & Reinterpret.AsByte(flags)) != 0
    }
    // you get the idea...        
}

You cannot constrain to enums. But the mathematical validity of these methods do not change if they are used with non enum types so you could allow them if you can determine that they are convertible to a struct of the relevant size.

ShuggyCoUk
  • 36,004
  • 6
  • 77
  • 101
0

below is code that benchmarks 4 different methods. results are shown in code in comment "BENCHMARK: .. nSec".

"((enum & flag) != 0)' is 10x faster than HasFlag() function. if you are in a tight loop, then i think it is best to accept it.

    public static int jumpCtr=0;
    public static int ctr=0;
    public static TestFlags gTestFlags = TestFlags.C;
    [Flags] public enum TestFlags { A=1<<1, B=1<<2, C=1<<3 }
    public static void Jump()  { jumpCtr++; gTestFlags = (gTestFlags == TestFlags.B) ? TestFlags.C : TestFlags.B;  }

    // IsEnumFlagPresent() https://stackoverflow.com/questions/987607/c-flags-enum-generic-function-to-look-for-a-flag
    [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool HasFlag_Faster<T>(T value, T lookingForFlag) 
        where T : struct
    {
        int intValue                = (int) (object) value;
        int intLookingForFlag       = (int) (object) lookingForFlag;
        return ((intValue & intLookingForFlag) != 0);
    }

    // IsEnumFlagPresent() https://stackoverflow.com/questions/987607/c-flags-enum-generic-function-to-look-for-a-flag
    [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool HasFlag_Faster_Integer(int intValue, int intLookingForFlag) 
    {
        return ((intValue & intLookingForFlag) != 0);
    }

    public static void Benchmark_HasFlag( )  
    {
        if ( ! hwDvr._weAreOnGswCpu) { return; }

        DateTime timer = DateTime.Now; 
        string a, b, c, d, e;
        double base_nSecPerLoop, b_nSecPerLoop, c_nSecPerLoop, d_nSecPerLoop, e_nSecPerLoop;
        int numOfLoops = (int) 1.0e6;

        //  ------------------------------------------------------
        for (int i=0; i<numOfLoops;i++) {
            Jump();
        }
        a = BenchMarkSystem_Helper.SimpleTimer_Loops( ref timer, numOfLoops, out base_nSecPerLoop);

        //  ------------------------------------------------------
        //  BENCHMARK: 50 nSec

        for (int i=0; i<numOfLoops;i++) {
            if (gTestFlags.HasFlag((TestFlags) TestFlags.C)) {   
                ctr++;
            }
            Jump();
        }
        b = BenchMarkSystem_Helper.SimpleTimer_Loops( ref timer, numOfLoops, out b_nSecPerLoop );

        double b_diff = b_nSecPerLoop - base_nSecPerLoop;

        //  ------------------------------------------------------
        //  BENCHMARK: 3 nSec

        for (int i=0; i<numOfLoops;i++) {
            if ((gTestFlags & TestFlags.C) != 0) {   
                ctr++;
            }
            Jump();
        }
        c = BenchMarkSystem_Helper.SimpleTimer_Loops( ref timer, numOfLoops, out c_nSecPerLoop );

        double c_diff = c_nSecPerLoop - base_nSecPerLoop;

        //  ------------------------------------------------------
        //  BENCHMARK: 64 nSec

        for (int i=0; i<numOfLoops;i++) {
            if (HasFlag_Faster<TestFlags>(value:gTestFlags, lookingForFlag: TestFlags.C)) {   
                ctr++;
            }
            Jump();
        }
        d = BenchMarkSystem_Helper.SimpleTimer_Loops( ref timer, numOfLoops, out d_nSecPerLoop );

        double d_diff = d_nSecPerLoop - base_nSecPerLoop;

        //  ------------------------------------------------------
        //  BENCHMARK: 14 nSec

        for (int i=0; i<numOfLoops;i++) {
            if (HasFlag_Faster_Integer((int)gTestFlags, (int)TestFlags.C)) {   
                ctr++;
            }
            Jump();
        }
        e = BenchMarkSystem_Helper.SimpleTimer_Loops( ref timer, numOfLoops, out e_nSecPerLoop );

        double e_diff = e_nSecPerLoop - base_nSecPerLoop;

        int brkPt=0;
    }
gsw
  • 1
0

Well, I don't believe there is a way to do this, as there are no constraints that apply to bitwise operators.

However... you can just cast your enum to int and do it.

public static Boolean IsEnumFlagPresent(int value,int lookingForFlag) 
{
    return ((value & lookingForFlag) == lookingForFlag);
}

This works, but may be confusing to someone.

  • note that this will not work for enums which are not ints... effective for the common case though – ShuggyCoUk Jun 12 '09 at 16:41
  • True, you would have to provide overloads for other value types. Also note that you can't just use ValueType as it doesn't work with bitwise operators. –  Jun 12 '09 at 17:43