Programmer
Programmer

Reputation: 125255

Enum Bitwise operation returning wrong value

I have color selection enum that represents Red, Blue, Green and None.

[Flags]
public enum SelectedColor
{
    None, Red, Blue, Green
}

When I create a new enum and set it to Red and Green then check if Blue is set, it returns true. Blue was never set anywhere.

For example:

SelectedColor selectedColor = SelectedColor.Red;
selectedColor |= SelectedColor.Green; //Add Green to Selection

//Check if blue is set
Debug.Log("Blue Selected hasFlag? : " + hasFlag(selectedColor, SelectedColor.Blue));

//Check if blue is set
Debug.Log("Blue Selected isSet? : " + isSet(selectedColor, SelectedColor.Blue));

Output:

Blue Selected hasFlag? : False

Blue Selected isSet? : True

hasFlag and isSet function:

bool hasFlag(SelectedColor source, SelectedColor value)
{
    int s1 = (int)source;
    return Convert.ToBoolean((s1 & Convert.ToInt32(((int)value) == s1)));
}


bool isSet(SelectedColor source, SelectedColor critValue)
{
    //return ((source & critValue) == critValue);
    return ((source & critValue) != 0);
}

As you can see, my isSet function is returning the wrong value. I have tried both return ((source & critValue) == critValue) and return ((source & critValue) != 0); in it but they both still failed. This should work according to my research on SO and this post.

My hasFlag function is fine but why is the isSet function returning the wrong value?

Please note that I am using .NET 3.5, so I can't use .NET 4 enum helper functions such as HasFlag.

Upvotes: 2

Views: 587

Answers (2)

user2299169
user2299169

Reputation:

Even though Matthew's answer is correct and it's a very good answer (there's a "maxed out version" of the topic here), I would keep it "generic" and a bit more simple and go another way: same behaviour used with Layer Masks. The limitation here, your enum can't have more than 32 items, though.

private void uncleFoo() {
    enum myNATO { Adam, Bravo, Charlie, Delta } //my enum with NATO codes
    int activeNATO = 0; //int to store selected NATO codes

    //some testing here
    activeNATO |= 1 << (int)myNATO.Adam;
    activeNATO |= 1 << (int)myNATO.Bravo;
    activeNATO |= 1 << (int)myNATO.Charlie;
    Debug.Log(activeNATO);
    Debug.Log("has  Adam? " + (activeNATO & 1 << (int)myNATO.Adam));
    Debug.Log("has Delta? " + (activeNATO & 1 << (int)myNATO.Delta));
}

Debug.Log will print "1" for Adam, and "0" for Delta, obviously.

Hope this helps ;)

Upvotes: 1

Matthew Watson
Matthew Watson

Reputation: 109567

If you do not specify values for enums, sequential numbers are assigned to to them like so:

[Flags]
public enum SelectedColor // WRONG
{
    None  = 0, // 000
    Red   = 1, // 001
    Blue  = 2, // 010
    Green = 3  // 011 <-- Not the next power of two!
}

Then this happens:

selectedColor = SelectedColor.Red; // 001
selectedColor |= SelectedColor.Green; // (001 | 011 ) == 011, which is still Green

You need to use powers of two for [Flags] enums, as follows:

[Flags]
public enum SelectedColor // CORRECT
{
    None  = 0, // 000
    Red   = 1, // 001
    Blue  = 2, // 010
    Green = 4  // 100
}

Then it works correctly:

selectedColor = SelectedColor.Red; // 001
selectedColor |= SelectedColor.Green; // (001 | 100) == 101, which is Red, Green 

Upvotes: 9

Related Questions