ne1410s
ne1410s

Reputation: 7102

Unexpected result in Enum.HasFlag

I recently encountered an error in my code where I was attempting to "dismantle" an enum flag into an array of its constituent values, but unexpected results would sometimes be returned.

Long story short, certain integer types assigned to my values appear to cause some behaviour that is unexpected (well, to me at least). I boiled down the issue into the following unit test:

public enum TestEnum
{
    ITEM1 = 30104,
    ITEM2 = 30201,
}

[TestClass]
public class EnumFlagTest
{
    [TestMethod]
    public void SanityCheck()
    {
        var flag2 = TestEnum.ITEM2;

        Assert.IsFalse(flag2.HasFlag(TestEnum.ITEM1));
    }
}

I did not expect flag2 to report that it "has the flag" of ITEM1, as I do not believe that it does.

I guess that it has something to do with the Int32 values I've assigned to the items, but please could someone explain what's going on?? - Why does this test fail?

Upvotes: 2

Views: 174

Answers (3)

theGleep
theGleep

Reputation: 1187

It looks like you are wanting to use "Bit flags" - a collection of boolean values that are easy to merge.

To do that, each flag needs one bit set and the bit needs to be in a unique location.

But it is valid to combine flags to cover common groupings.

One technique I learned here on StackOverflow is to set the first flag and then bit-shift for the rest of the flags. Something like this:

[Flags]
enum color {
  none = 0,
  red = 1,                           // 0b0001
  yellow = red << 1,                 // 0b0010
  blue = yellow << 1,                // 0b0100

  primaries = red | yellow | blue,   // 0b0111  

  green = yellow | blue,             // 0b0110
  purple = red | blue,               // 0b0101
  orange = red | yellow              // 0b0011
}

with this, you can

Assert.IsFalse(Color.green.HasFlag(color.red));
Assert.IsTrue(Color.primary.HasFlag(color.red));

I hope that clears things up for you!

  • Note, the code here is largely pseudo-code; there may be syntax errors. The concept is, however, sound.

Upvotes: 1

DavidG
DavidG

Reputation: 119206

The HasFlag method is a bitwise check and the bits of ITEM1 match up with ITEM2:

Note that ITEM2 contains all the same 1s that ITEM1 has:

ITEM1: 111010110011000
ITEM2: 111010111111001

Upvotes: 2

Jon Skeet
Jon Skeet

Reputation: 1504122

Basically, you shouldn't use HasFlag for a non flags-based enum... and a flags-based enum should use a separate bit for each flag.

The problem is indeed because of the values. Writing this in binary, you've got:

public enum TestEnum
{
    ITEM1 = 0b111010110011000,
    ITEM2 = 0b111010111111001
}

Note how every bit that's set in ITEM1 is also set in ITEM2 - and that's what's checked by HasFlag. From the documentation:

Returns:

true if the bit field or bit fields that are set in flag are also set in the current instance; otherwise, false.

That is true for TestEnum.ITEM2.HasFlag(TestEnum.Item1), hence it returns true.

Upvotes: 4

Related Questions