How to Compare Flags in C#?

88,941

Solution 1

In .NET 4 there is a new method Enum.HasFlag. This allows you to write:

if ( testItem.HasFlag( FlagTest.Flag1 ) )
{
    // Do Stuff
}

which is much more readable, IMO.

The .NET source indicates that this performs the same logic as the accepted answer:

public Boolean HasFlag(Enum flag) {
    if (!this.GetType().IsEquivalentTo(flag.GetType())) {
        throw new ArgumentException(
            Environment.GetResourceString(
                "Argument_EnumTypeDoesNotMatch", 
                flag.GetType(), 
                this.GetType()));
    }

    ulong uFlag = ToUInt64(flag.GetValue()); 
    ulong uThis = ToUInt64(GetValue());
    // test predicate
    return ((uThis & uFlag) == uFlag); 
}

Solution 2

if ((testItem & FlagTest.Flag1) == FlagTest.Flag1)
{
     // Do something
}

(testItem & FlagTest.Flag1) is a bitwise AND operation.

FlagTest.Flag1 is equivalent to 001 with OP's enum. Now let's say testItem has Flag1 and Flag2 (so it's bitwise 101):

  001
 &101
 ----
  001 == FlagTest.Flag1

Solution 3

For those who have trouble visualizing what is happening with the accepted solution (which is this),

if ((testItem & FlagTest.Flag1) == FlagTest.Flag1)
{
    // Do stuff.
}

testItem (as per the question) is defined as,

testItem 
 = flag1 | flag2  
 = 001 | 010  
 = 011

Then, in the if statement, the left hand side of the comparison is,

(testItem & flag1) 
 = (011 & 001) 
 = 001

And the full if statement (that evaluates to true if flag1 is set in testItem),

(testItem & flag1) == flag1
 = (001) == 001
 = true

Solution 4

@phil-devaney

Note that except in the simplest of cases, the Enum.HasFlag carries a heavy performance penalty in comparison to writing out the code manually. Consider the following code:

[Flags]
public enum TestFlags
{
    One = 1,
    Two = 2,
    Three = 4,
    Four = 8,
    Five = 16,
    Six = 32,
    Seven = 64,
    Eight = 128,
    Nine = 256,
    Ten = 512
}


class Program
{
    static void Main(string[] args)
    {
        TestFlags f = TestFlags.Five; /* or any other enum */
        bool result = false;

        Stopwatch s = Stopwatch.StartNew();
        for (int i = 0; i < 10000000; i++)
        {
            result |= f.HasFlag(TestFlags.Three);
        }
        s.Stop();
        Console.WriteLine(s.ElapsedMilliseconds); // *4793 ms*

        s.Restart();
        for (int i = 0; i < 10000000; i++)
        {
            result |= (f & TestFlags.Three) != 0;
        }
        s.Stop();
        Console.WriteLine(s.ElapsedMilliseconds); // *27 ms*        

        Console.ReadLine();
    }
}

Over 10 million iterations, the HasFlags extension method takes a whopping 4793 ms, compared to the 27 ms for the standard bitwise implementation.

Solution 5

I set up an extension method to do it: related question.

Basically:

public static bool IsSet( this Enum input, Enum matchTo )
{
    return ( Convert.ToUInt32( input ) & Convert.ToUInt32( matchTo ) ) != 0;
}

Then you can do:

FlagTests testItem = FlagTests.Flag1 | FlagTests.Flag2;

if( testItem.IsSet ( FlagTests.Flag1 ) )
    //Flag1 is set

Incidentally the convention I use for enums is singular for standard, plural for flags. That way you know from the enum name whether it can hold multiple values.

Share:
88,941

Related videos on Youtube

David Basarab
Author by

David Basarab

David Basarab Software craftsman that is constantly scrubbing my code.

Updated on February 16, 2020

Comments

  • David Basarab
    David Basarab over 4 years

    I have a flag enum below.

    [Flags]
    public enum FlagTest
    {
        None = 0x0,
        Flag1 = 0x1,
        Flag2 = 0x2,
        Flag3 = 0x4
    }
    

    I cannot make the if statement evaluate to true.

    FlagTest testItem = FlagTest.Flag1 | FlagTest.Flag2;
    
    if (testItem == FlagTest.Flag1)
    {
        // Do something,
        // however This is never true.
    }
    

    How can I make this true?

    • Roy Lee
      Roy Lee over 11 years
      Correct me if I'm wrong, is 0 appropriate to be used as flag value?
    • Andy
      Andy about 11 years
      @Roylee: 0 is acceptable, and it's a good idea to have a "None" or "Undefined" flag in order to test having no flags set. It's by no means required, but it's a good practice. The important thing to remember about this is pointed out by Leonid in his answer.
    • ThatMatthew
      ThatMatthew about 11 years
      @Roylee It is actually recommended by Microsoft to provide a None flag with a value of zero. See msdn.microsoft.com/en-us/library/vstudio/…
    • MikeT
      MikeT over 10 years
      A lot of people also argue that bit comparison is too difficult to read so should be avoided in favour of an collection of flags, where you can just do collection.contains flag
    • Barkah
      Barkah over 6 years
      You were quite close, except you have to invert you logic, you need the bitwise & operator for comparison, | is like an addition: 1|2=3,5|2=7, 3&2=2, 7&2=2, 8&2=0. 0 evaluates to false, everything else to true.
  • user276648
    user276648 about 14 years
    This should be a comment but as I'm a new user it looks like I just can't add comments yet... public static bool IsSet( this Enum input, Enum matchTo ) { return ( Convert.ToUInt32( input ) & Convert.ToUInt32( matchTo ) ) != 0; } Is there a way to be compatible with any kind of enum (because here it won't work if your enum is of type UInt64 or can have negative values)?
  • Rob van Groenewoud
    Rob van Groenewoud about 14 years
    Ah, finally something out of the box. This is great, I've been waiting for this relatively simple feature for a long time. Glad they decided to slip it in.
  • user276648
    user276648 over 12 years
    Indeed. If you look at the implementation of HasFlag, you'll see that it does a "GetType()" on both operands, which is quite slow. Then it does "Enum.ToUInt64(value.GetValue());" on both operands before doing the bitwise check.
  • Andy Mortimer
    Andy Mortimer over 12 years
    Note, however, the answer below showing performance issues with this method -- this might be an issue for some people. Happily not for me.
  • PPC
    PPC about 12 years
    This is quite redundant with Enum.HasFlag(Enum) (available in .net 4.0)
  • Keith
    Keith about 12 years
    @PPC I wouldn't say redundant exactly - plenty of people are developing on older versions of the framework. You are right though, .Net 4 users should use the HasFlag extension instead.
  • PPC
    PPC about 12 years
    @Keith: Also, there is a notable difference: ((FlagTest)0x1).HasFlag(0x0) will return true, which may or may not be a wanted behaviour
  • PPC
    PPC about 12 years
    This is a huge performance difference for a very small functionality gain. Worth stopping using HasFlag()
  • Adam Houldsworth
    Adam Houldsworth over 11 years
    The performance consideration for this method is boxing because it takes arguments as an instance of the Enum class.
  • MarioVW
    MarioVW almost 11 years
    I ran your test several times and got ~500ms for HasFlags and ~32ms for bitwise. While still an order of magnitude faster with bitwise, HasFlags was an order of magnitude down from your test. (Ran the test on a 2.5GHz Core i3 and .NET 4.5)
  • Bob
    Bob almost 11 years
    @MarioVW Running several times on .NET 4, i7-3770 gives ~2400ms vs ~20ms on AnyCPU (64-bit) mode, and ~3000ms vs ~20ms on 32-bit mode. .NET 4.5 may have optimised it slightly. Also note the performance difference between 64-bit and 32-bit builds, which might be due to faster 64-bit arithmetic (see the first comment).
  • Ian R. O'Brien
    Ian R. O'Brien about 10 years
    What exactly is the logic here? Why does the predicate have to be written like this?
  • Slipp D. Thompson
    Slipp D. Thompson over 9 years
    («flag var» & «flag value») != 0 doesn't work for me. The condition always fails and my compiler (Unity3D's Mono 2.6.5) reports a “warning CS0162: Unreachable code detected” when used in an if (…).
  • Chuck Dee
    Chuck Dee over 9 years
    @SlippD.Thompson - glad that you were able to get something that runs for you. In default .net it works. Mono has differences, and perhaps you ran afoul of one.
  • Slipp D. Thompson
    Slipp D. Thompson over 9 years
    Sarcasm? And no, the code example I gave also fails in Microsoft Visual Studio Express 2013 on Windows 8. Scapegoating Mono is not the correct solution.
  • Chuck Dee
    Chuck Dee over 9 years
    @SlippD.Thompson - No, no sarcasm. Just answering the question. And I just ran it in linqpad to make sure I wasn't missing anything. But with that, and the fact that we're not supposed to discuss in comments, I'm out.
  • Slipp D. Thompson
    Slipp D. Thompson over 9 years
    @wraith808: I realized the mistake was was making with my test that you have correct in yours— the power of 2 … = 1, … = 2, … = 4 on the enum values are critically important when using [Flags]. I was assuming that it would start the entries at 1 and advance by Po2s automatically. Behavior looks consistent across MS .NET and Unity's dated Mono. My apologies putting this on you.
  • thersch
    thersch about 9 years
    I just fixed such a 0-flag bug. I think this is a design error in .NET framework (3.5) because you need to know which of the flag values is 0 before testing it.
  • pqsk
    pqsk about 9 years
    @IanR.O'Brien Flag1 | Flag 2 translates to 001 or 010 which is the same as 011, now if you do an equality of that 011 == Flag1 or translated 011 == 001, that returns false always. Now if you do a bitwise AND with Flag1 then it translates to 011 AND 001 which returns 001 now doing the equality returns true, because 001 == 001
  • Maxence
    Maxence almost 9 years
    For informations on the performance issue, look at this answer: stackoverflow.com/q/7368652/200443
  • Sebastian Xawery Wiśniowiecki
    Sebastian Xawery Wiśniowiecki over 8 years
    It is the best solution because HasFlags is much more resource-intensive. And moreover everything what HasFlags is doing, here is done by compiler