How to Compare Flags in C#

How to Compare Flags in C#?

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);
}

How to check if any flags of a flag combination are set?

If you want to know if letter has any of the letters in AB you must use the AND & operator. Something like:

if ((letter & Letters.AB) != 0)
{
// Some flag (A,B or both) is enabled
}
else
{
// None of them are enabled
}

Which is the better way to compare Flags Enum?

So long as flag is a one-bit flag, they are equivalent. If flag has multiple bits,

(value & flag) == flag;

is a logical AND (ALL bits must match) while

(value & flag) != 0;

is a logical OR (ANY of the bits must match).

Comparing enum flags in C#

Personally, I think that look fine because you've wrapped it into a single purpose function. If you had that code scattered through an entire program I think you would have some problems, but what you've created improves clarity everywhere it is used and the function itself is clear enough what it does.

Just my opinion of course.

You could though, use the is keyword, which might help a little

public static bool IsSet<T>(this T value, T flags) where T : Enum
{
if (value is int)
{
return ((int)(object)a & (int)(object)b) == (int)(object)b);
}
//etc...

C# Enum Flags Comparison

Change your inner & to |:

if ((operation & (Operations.add | Operations.eval)) == (Operations.add | Operations.eval))

This is equivalent to:

if( ((operation & Operations.add)==Operations.add) &&
((operation & Operations.eval)==Operations.eval))

which might be more readable. You might also want to consider an Extension like this:

public static bool HasFlag(this Operations op, Operations checkflag)
{
return (op & checkflag)==checkflag;
}

then you can do this:

if(operation.HasFlag(Operations.add) && Operations.HasFlag(Operations.eval))

which might be even more readable. Finally you could create this extension for even more fun:

public static bool HasAllFlags(this Operations op, params Operations[] checkflags)
{
foreach(Operations checkflag in checkflags)
{
if((op & checkflag)!=checkflag)
return false;
}
return true;
}

Then your expression could turn into:

if(operation.HasAllFlags(Operations.add, Operations.eval))

How to compare standard DayOfWeek to own with Flag

Since your enum uses the same order of days as the built-in DayOfWeek, all you need to do is to use the variable of DayOfWeek type as the exponent of 2 and then bitwise-AND it with your enum variable.

Something like this (this will check if Monday flag of your enum is 1 or not):

MyDayOfWeek Days = MyDayOfWeek.Monday | MyDayOfWeek.Friday;
DayOfWeek D = DayOfWeek.Monday;
var Mask = 1 << (int)D;

if ((Mask & (int)Days) == Mask)
//Do whatever;

I have renamed your enum to MyDaysOfWeek, whereas DayOfWeek is the built-in .NET type. You can do the same for any day of week.

Edit

As @HenkHolterman pointed out (thanks for that), you'll have problems with your Sunday set to 0. A Flags enum should normally start with a member named None that is equal to 0 and which indicates that none of the flags of the variable are set.

How to compare an enum flag to a Dictionary key?

First, I should point out that if:

Levels keyA = Levels.Beginner | Levels.Medium | Levels. Advanced;
Levels keyB = Levels.Medium;

then:

Debug.Assert(keyA.GetHashCode() != keyB.GetHashCode());
Debug.Assert(keyA != keyB);

When looking up values in the dictionary, the dictionary first uses the hash value of the key to determine the correct bucket, and then uses equality comparison to identify the right key in the bucket.

If the hash values are not equal then the key will not be found. If the key values are not equal, then the value will not be found.

You can get all entries that have a key containing Levels.Medium by seeing if it's bit pattern is present in the key with the following LINQ expression:

var mediumEntries = Bank.Where(entry => 0 != ((int)entry.Key & (int)Levels.Medium));

Or, as @Ria pointed out, in .Net 4 you can use the HasFlags member:

var mediumEntries = Bank.Where(entry => entry.Key.HasFlag(Levels.Medium));

The good point was made in another answers (@dasblinkenlight, @Ria) that the values of your enumeration need to have non-overlapping bit patterns for this to work:

[Flags()]        
public enum Levels
{
Beginner = 0x01,
Medium = 0x02,
Advanced = 0x04,
Master = 0x08
}

How to Compare Flags in C#? (part 2)

With

x.Type == showType

you will only get the items that match all the conditions (bit flags) exactly. With

(x.Type & showType) != 0

You will find all items that have at least a 1 bit match with showType, which is probably what you want.

What does the [Flags] Enum Attribute mean in C#?

The [Flags] attribute should be used whenever the enumerable represents a collection of possible values, rather than a single value. Such collections are often used with bitwise operators, for example:

var allowedColors = MyColor.Red | MyColor.Green | MyColor.Blue;

Note that the [Flags] attribute doesn't enable this by itself - all it does is allow a nice representation by the .ToString() method:

enum Suits { Spades = 1, Clubs = 2, Diamonds = 4, Hearts = 8 }
[Flags] enum SuitsFlags { Spades = 1, Clubs = 2, Diamonds = 4, Hearts = 8 }

...

var str1 = (Suits.Spades | Suits.Diamonds).ToString();
// "5"
var str2 = (SuitsFlags.Spades | SuitsFlags.Diamonds).ToString();
// "Spades, Diamonds"

It is also important to note that [Flags] does not automatically make the enum values powers of two. If you omit the numeric values, the enum will not work as one might expect in bitwise operations, because by default the values start with 0 and increment.

Incorrect declaration:

[Flags]
public enum MyColors
{
Yellow, // 0
Green, // 1
Red, // 2
Blue // 3
}

The values, if declared this way, will be Yellow = 0, Green = 1, Red = 2, Blue = 3. This will render it useless as flags.

Here's an example of a correct declaration:

[Flags]
public enum MyColors
{
Yellow = 1,
Green = 2,
Red = 4,
Blue = 8
}

To retrieve the distinct values in your property, one can do this:

if (myProperties.AllowedColors.HasFlag(MyColor.Yellow))
{
// Yellow is allowed...
}

or prior to .NET 4:

if((myProperties.AllowedColors & MyColor.Yellow) == MyColor.Yellow)
{
// Yellow is allowed...
}

if((myProperties.AllowedColors & MyColor.Green) == MyColor.Green)
{
// Green is allowed...
}

Under the covers

This works because you used powers of two in your enumeration. Under the covers, your enumeration values look like this in binary ones and zeros:

 Yellow: 00000001
Green: 00000010
Red: 00000100
Blue: 00001000

Similarly, after you've set your property AllowedColors to Red, Green and Blue using the binary bitwise OR | operator, AllowedColors looks like this:

myProperties.AllowedColors: 00001110

So when you retrieve the value you are actually performing bitwise AND & on the values:

myProperties.AllowedColors: 00001110
MyColor.Green: 00000010
-----------------------
00000010 // Hey, this is the same as MyColor.Green!

The None = 0 value

And regarding the use of 0 in your enumeration, quoting from MSDN:

[Flags]
public enum MyColors
{
None = 0,
....
}

Use None as the name of the flag enumerated constant whose value is zero. You cannot use the None enumerated constant in a bitwise AND operation to test for a flag because the result is always zero. However, you can perform a logical, not a bitwise, comparison between the numeric value and the None enumerated constant to determine whether any bits in the numeric value are set.

You can find more info about the flags attribute and its usage at msdn and designing flags at msdn



Related Topics



Leave a reply



Submit