How to Compare Flags in C#?
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.
Related videos on Youtube
David Basarab
David Basarab Software craftsman that is constantly scrubbing my code.
Updated on February 16, 2020Comments
-
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 over 11 yearsCorrect me if I'm wrong, is 0 appropriate to be used as flag value?
-
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 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 over 10 yearsA 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 over 6 yearsYou 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 tofalse
, everything else totrue
.
-
-
user276648 about 14 yearsThis 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 about 14 yearsAh, 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 over 12 yearsIndeed. 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 over 12 yearsNote, however, the answer below showing performance issues with this method -- this might be an issue for some people. Happily not for me.
-
PPC about 12 yearsThis is quite redundant with Enum.HasFlag(Enum) (available in .net 4.0)
-
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 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 about 12 yearsThis is a huge performance difference for a very small functionality gain. Worth stopping using HasFlag()
-
Adam Houldsworth over 11 yearsThe performance consideration for this method is boxing because it takes arguments as an instance of the
Enum
class. -
MarioVW almost 11 yearsI 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 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 about 10 yearsWhat exactly is the logic here? Why does the predicate have to be written like this?
-
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 anif (…)
. -
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 over 9 yearsSarcasm? 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 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 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 at1
and advance by Po2s automatically. Behavior looks consistent across MS .NET and Unity's dated Mono. My apologies putting this on you. -
thersch about 9 yearsI 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 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 almost 9 yearsFor informations on the performance issue, look at this answer: stackoverflow.com/q/7368652/200443
-
Sebastian Xawery Wiśniowiecki over 8 yearsIt is the best solution because HasFlags is much more resource-intensive. And moreover everything what HasFlags is doing, here is done by compiler