4.6. Determining Whether One or More Enumeration Flags Are Set

Problem

You need to determine whether a variable of an enumeration type, consisting of bit flags, contains one or more specific flags. For example, given the following enumeration Language:

[Flags]
enum Language
{
    CSharp = 0x0001, VBNET = 0x0002, VB6 = 0x0004, Cpp = 0x0008
}

Determine, using Boolean logic, whether the variable lang in the following line of code contains a language such as Language.CSharp and/or Language.Cpp:

Language lang = Language.CSharp | Language.VBNET;

Solution

To determine whether a variable contains a single bit flag that is set, use the following conditional:

if((lang & Language.CSharp) == Language.CSharp)
{
    // lang contains at least Language.CSharp
}

To determine whether a variable exclusively contains a single bit flag that is set, use the following conditional:

if(lang == Language.CSharp)
{
    // lang contains only the Language.CSharp
}

To determine whether a variable contains a set of bit flags that are all set, use the following conditional:

if((lang & (Language.CSharp | Language.VBNET)) == 
   (Language.CSharp | Language.VBNET))
{
    // lang contains at least Language.CSharp and Language.VBNET
}

To determine whether a variable exclusively contains a set of bit flags that are all set, use the following conditional:

if((lang | (Language.CSharp | Language.VBNET)) == 
   (Language.CSharp | Language.VBNET))
{
    // lang contains only the Language.CSharp and Language.VBNET
}

Discussion

When enumerations are used as bit flags (these enumerations should be marked with the Flags attribute) they usually will require some kind of conditional testing to be performed. This kind of conditional testing requires the use of the bitwise AND (&) and OR (|) operators.

Testing for a variable having a specific bit flag set is done with the following conditional statement:

if((lang & Language.CSharp) == Language.CSharp)

where lang is of the Language enumeration type.

The & operator is used with a bitmask to determine whether a bit is set to 1. The result of ANDing two bits is 1 only when both bits are 1; otherwise, the result is 0. We can use this operation to determine if a specific bit flag is set to a 1 in the number containing the individual bit flags. If we AND the variable lang with the specific bit flag we are testing for (in this case Language.CSharp), we can extract that single specific bit flag. The expression (lang & Language.CSharp) is solved in the following manner if lang is equal to Language.CSharp:

Language.CSharp      0001
lang                 0001
ANDed bit values     0001

If lang is equal to another value such as Language.VBNET, the expression is solved in the following manner:

Language.CSharp      0001
lang                 0010
ANDed bit values     0000

Notice that ANDing the bits together returns the value Language.CSharp in the first expression and 0x0000 in the second expression. Comparing this result to the value we are looking for (Language.CSharp) tells us whether that specific bit was turned on.

This method is great for checking specific bits, but what if you wanted to know if only a specific bit was turned on (and all other bits turned off) or off (and all other bits turned on)? To test whether only the Language.CSharp bit is turned on in the variable lang, we can use the following conditional statement:

if(lang == Language.CSharp)

Consider the situation if the variable lang contained only the value Language.CSharp. The expression using the OR operator would look like this:

Language.CSharp      0001
lang                 0001
ORed bit values      0001

Now, add a language value or two to the variable lang and perform the same operation on lang:

Language.CSharp      0001
lang                 1101
ORed bit values      1101

The first expression results in the same value as we are testing against. The second expression results in a much larger value than Language.CSharp. This result indicates that the variable lang in the first expression only contains the value Language.CSharp and the second expression contains other languages including Language.CSharp.

Using this same formula, we can test multiple bits to determine whether they are both on and all other bits are off. This is done in the following conditional statement:

if((lang & (Language.CSharp | Language.VBNET)) == 
   (Language.CSharp | Language.VBNET))

Notice that to test for more than one language, we simply OR the language values together. By switching the first & operator for an | operator, we can determine whether at least these bits are turned on. This is done in the following conditional statement:

if((lang | (Language.CSharp | Language.VBNET)) == 
   (Language.CSharp | Language.VBNET))

When testing for multiple enumeration values, it may be beneficial to add a value to your enumeration, which ORs together all the values you want to test for. If we wanted to test for all languages except Language.CSharp, our conditional statement(s) would grow quite large and unwieldy. To fix this, we could add a value to the Language enumeration that ORs together all languages except Language.CSharp. The new enumeration would look like this:

[Flags]
enum Language
{
    CSharp = 0x0001, VBNET = 0x0002, VB6 = 0x0004, Cpp = 0x0008,
    AllLanguagesExceptCSharp = VBNET | VB6 | Cpp
}

and our conditional statement might look similar to the following:

if((lang | Language.AllLanguagesExceptCSharp) == 
   Language. AllLanguagesExceptCSharp)

This statement is quite a bit smaller and is easier to manage and read.

Tip

Use the AND operator when testing whether one or more bits are set to 1. Use the OR operator when testing whether one or more bits are set to 0.

Get C# Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.