How to Compare Flags in C#?
Asked Answered
L

11

163

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?

Lade answered 2/9, 2008 at 18:28 Comment(5)
Correct me if I'm wrong, is 0 appropriate to be used as flag value?Straightout
@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.Teredo
@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/…Graham
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 flagPotation
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.Calathus
R
336

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); 
}
Raymonraymond answered 20/11, 2009 at 11:4 Comment(4)
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.Bind
Note, however, the answer below showing performance issues with this method -- this might be an issue for some people. Happily not for me.Intrench
The performance consideration for this method is boxing because it takes arguments as an instance of the Enum class.Carl
For informations on the performance issue, look at this answer: https://mcmap.net/q/22279/-what-is-it-that-makes-enum-hasflag-so-slow/200443Schlueter
H
188
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
Hannis answered 2/9, 2008 at 18:31 Comment(3)
What exactly is the logic here? Why does the predicate have to be written like this?Influence
@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 == 001Candicecandid
It is the best solution because HasFlags is much more resource-intensive. And moreover everything what HasFlags is doing, here is done by compilerPulsate
S
79

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
Scour answered 20/11, 2009 at 11:12 Comment(0)
B
26

@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.

Barthel answered 23/8, 2011 at 16:25 Comment(9)
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.Cheese
This is a huge performance difference for a very small functionality gain. Worth stopping using HasFlag()Corolla
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)Rees
@Rees 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).Grandee
(«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 (…).Parton
@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.Barthel
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.Parton
@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.Barthel
@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.Parton
H
21

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.

Hayfield answered 2/9, 2008 at 18:38 Comment(4)
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)?Cheese
This is quite redundant with Enum.HasFlag(Enum) (available in .net 4.0)Corolla
@Corolla 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.Hayfield
@Keith: Also, there is a notable difference: ((FlagTest)0x1).HasFlag(0x0) will return true, which may or may not be a wanted behaviourCorolla
W
19

One more piece of advice... Never do the standard binary check with the flag whose value is "0". Your check on this flag will always be true.

[Flags]
public enum LevelOfDetail
{
    [EnumMember(Value = "FullInfo")]
    FullInfo=0,
    [EnumMember(Value = "BusinessData")]
    BusinessData=1
}

If you binary check input parameter against FullInfo - you get:

detailLevel = LevelOfDetail.BusinessData;
bool bPRez = (detailLevel & LevelOfDetail.FullInfo) == LevelOfDetail.FullInfo;

bPRez will always be true as ANYTHING & 0 always == 0.


Instead you should simply check that the value of the input is 0:

bool bPRez = (detailLevel == LevelOfDetail.FullInfo);
Weathers answered 5/8, 2010 at 7:10 Comment(1)
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.Fauve
L
7
if((testItem & FlagTest.Flag1) == FlagTest.Flag1) 
{
...
}
Literal answered 2/9, 2008 at 18:32 Comment(0)
E
5

For bit operations, you need to use bitwise operators.

This should do the trick:

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

Edit: Fixed my if check - I slipped back into my C/C++ ways (thanks to Ryan Farley for pointing it out)

Efficacy answered 2/9, 2008 at 18:30 Comment(0)
C
5

Regarding the edit. You can't make it true. I suggest you wrap what you want into another class (or extension method) to get closer to the syntax you need.

i.e.

public class FlagTestCompare
{
    public static bool Compare(this FlagTest myFlag, FlagTest condition)
    {
         return ((myFlag & condition) == condition);
    }
}
Cushing answered 2/9, 2008 at 18:44 Comment(0)
P
4

Try this:


if ((testItem & FlagTest.Flag1) == FlagTest.Flag1)
{
    // do something
}
Basically, your code is asking if having both flags set is the same as having one flag set, which is obviously false. The code above will leave only the Flag1 bit set if it is set at all, then compares this result to Flag1.
Perdita answered 2/9, 2008 at 18:33 Comment(0)
R
1

even without [Flags], you could use something like this

if((testItem & (FlagTest.Flag1 | FlagTest.Flag2 ))!=0){
//..
}

or if you have a Zero value enum

if((testItem & (FlagTest.Flag1 | FlagTest.Flag2 ))!=FlagTest.None){
//..
}
Roue answered 19/11, 2015 at 18:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.