Testing for bitwise Enum values

17,225

Solution 1

Short answer: Yes :)

Longer:

1) All operations are performed on the integer value of the flags variable, so you can think about them in terms of this.

2) Yes.

3) Either works. However, it's worth noting that if someone shoves an invalid value into a variable then the == TrainingComponentTypes.All version will fail. For example:

var badValue = (TrainingComponentTypes)128 | TrainingComponentTypes.All;
// now badValue != TrainingComponentTypes.All
// but (badValue & TrainingComponentTypes.All) == TrainingComponentTypes.All

For this part:

I am not sure if we'd ever receive a value where All was combined with other TrainingComponentTypes.

I'm not sure you fully understand how the enum works under the covers.

The value of All is:
    127 = 1111111 (binary)

The other values are:
    AccreditedCourse       = 0000001
    Qualification          = 0000010
    Unit                   = 0000100
    SkillSet               = 0001000
    UnitContextualisation  = 0010000
    TrainingPackage        = 0100000
    AccreditedCourseModule = 1000000

As you can see, All is simply the bitwise | of all these values together. You can't combine any other TraningComponentTypes with All, because All already includes them! Also, if you combine them all together with | yourself it's exactly the same as using All directly (so, All is simply a convenience when you define it inside an enum).

4) You could use it to check for None or All but not for other values.

It's worth noting that there is a convenience method on Enum that will do these checks for you: Enum.HasFlag.

Solution 2

Is my understanding of a logical comparison correct given my example above?

Yes, logical in this context means the equality and inequality operators.

Is the way I am performing a bitwise comparison correct?

Yes, but there is an easier way: Enum.HasFlag. For example:

tct.HasFlag(TrainingComponentTypes.Qualification)

instead of:

(tct & TrainingComponentTypes.Qualification) == TrainingComponentTypes.Qualification

What is the right way to handle the "All" value (bitwise or logical). I am not sure if we'd ever receive a value where All was combined with other TrainingComponentTypes. I can't see why we would, but then, you never know?

I think it is better to define All in the enum itself as the bitwise OR of all its parts. But you'll see people do it both ways.

Am I right in assuming that switch statements basically shouldn't be used for bitwise enums (given none is appears to be a special case and requires a logical comparison)?

No, not at all. Feel free to use them is switch statements. The case values must be constants but they can be expressions and are tested for equality. The compiler will tell you if you do something silly like try to use the same case value twice.

Solution 3

  1. Yes.

  2. Yes

  3. Both logical and bitwise could be used. Usage depends on whether all is all bits set or just the bitwise OR of all the values you've defined.

  4. Yes, but not because of None. A switch compares a single value, whereas a bit field can obviously have multiple values.

As others have noted Enum contains HasFlag().

Share:
17,225

Related videos on Youtube

Mr Moose
Author by

Mr Moose

Updated on January 29, 2021

Comments

  • Mr Moose
    Mr Moose over 3 years

    I haven't really used bitwise enums before, and I just want to make sure my testing is correct. I am most interested in testing for the values None and All. We receive data from a webservice that utilises this enum to categorise certain pieces of the data. Given that, I am assuming that nether None nor All would ever be combined with any other value.

    Given the following bitwise enum definition;

    [System.FlagsAttribute()]
    public enum TrainingComponentTypes : int
        {
            None = 0,
            AccreditedCourse = 1,
            Qualification = 2,
            Unit = 4,
            SkillSet = 8,
            UnitContextualisation = 16,
            TrainingPackage = 32,
            AccreditedCourseModule = 64,
            All = 127,
        }
    

    I read the following quote on this MSDN site about FlagAttributes;

    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.

    Does a logical comparison in this instance refer to a normal equality test for enums? For example;

    TrainingComponentTypes tct = TrainingComponentTypes.None; 
    if (tct == TrainingComponentTypes.None) 
    { ... }
    

    For a bitwise comparison, I am performing the following;

     TrainingComponentTypes tct = TrainingComponentTypes.AccreditedCourse | TrainingComponentTypes.Qualification | TrainingComponentTypes.TrainingPackage;
     Assert.IsTrue((tct & TrainingComponentTypes.AccreditedCourse) == TrainingComponentTypes.AccreditedCourse, "Expected AccreditedCourse as part the enum");
    
     Assert.IsFalse((tct & TrainingComponentTypes.SkillSet) == TrainingComponentTypes.SkillSet, "Found unexpected SkillSet as part the enum");
    

    Lastly, when testing for all, I have tried both a logical, and bitwise comparison, and they both return the same. Should I be using one over the other here? For example;

    TrainingComponentTypes tct = TrainingComponentTypes.All;
    
    Assert.IsTrue((tct & TrainingComponentTypes.All) == TrainingComponentTypes.All, "Expected All as part the enum");
    Assert.IsTrue((tct) == TrainingComponentTypes.All, "Expected All as part the enum");
    // The follow also pass the assertion for a value of All
    Assert.IsTrue((tct & TrainingComponentTypes.Qualification) == TrainingComponentTypes.Qualification, "Expected Qualification as part the enum");
    Assert.IsTrue((tct & TrainingComponentTypes.TrainingPackage) == TrainingComponentTypes.TrainingPackage, "Expected TrainingPackage as part the enum");
    

    So in summary, I'd like to know the following about Bitwise enums;

    1. Is my understanding of a logical comparison correct given my example above?
    2. Is the way I am performing a bitwise comparison correct?
    3. What is the right way to handle the "All" value (bitwise or logical). I am not sure if we'd ever receive a value where All was combined with other TrainingComponentTypes. I can't see why we would, but then, you never know?
    4. Am I right in assuming that switch statements basically shouldn't be used for bitwise enums (given none is appears to be a special case and requires a logical comparison)?

    Thanks, Chris

  • Mr Moose
    Mr Moose almost 13 years
    Thanks for the detailed answer. Most answers for this question provide pretty similar explanations, but yours is complete with detailed explanations.
  • Mr Moose
    Mr Moose almost 13 years
    Yes. Thanks. I haven't used bitwise enums before so this is a bit new to me. Takes me back to first year Uni, but that was a while ago.
  • Mr Moose
    Mr Moose almost 13 years
    Thanks for the detailed info. I am using .NET 3.5 for this particular project, and I don't believe I can use Enum.HasFlag with this version. The documentation seems to show it for .NET 4.0 but nothing prior.
  • Mr Moose
    Mr Moose almost 13 years
    - "Usage depends on whether all is all bits set or just the bitwise OR of all the vlaues you've defined" - Ah great. Thanks for the info.
  • keeehlan
    keeehlan over 10 years
    I've heard people say that using Enum.HasFlag isn't the best way to check bitwise values, but I often disagree. Can anybody attest to why Enum.HasFlag might not work as well as manually checking?
  • Rick Sladkey
    Rick Sladkey over 10 years
    Enum.HasFlag was pathologically slow when it was first added but has since been improved. Also see this answer
  • stigzler
    stigzler about 4 years
    This - lot nicer.