Flags Enumerations

Definition of Flags Enumerators

A flags enumerator enables a series of boolean values to be tied to a single datatype. Instead of assigning one of a predefined series of values to a variable, it will instead store a byte that results from the concatenation of all possible values in the series.

Unlike traditional enumerators, the actual values in a flags enumerator must be in sequence for it to work correctly. This does not refer to a base 10 sequence, but instead to a base 2 sequence. This means that the integer value expressed as base 2 must be as shown (for five values in this example): 00001 00010 00100 01000 10000. Expressed in base 10, this is 1, 2, 4, 8, 16.

You may use an empty value, but the empty value will always be false if the item contains a value. These options are concatenated by using the bitwise or operator "|" in C#, "Or" in VB.NET.

Building a Flags Enum

Say you want to express a morning checklist of five items as a flags enumerator. This might be the definition:

// C#
[ flags() ]
enum todo

{
   do_nothing           = 0,
   walk_dog             = 1,
   cook_breakfast       = 2,
   deliver_newspaper    = 4,
   visit_miss_kerbopple = 8,
   wash_covers          = 16
}
'' VB.NET
<flags()> _
Enum todo
   do_nothing           = 0
   walk_dog             = 1
   cook_breakfast       = 2
   deliver_newspaper    = 4
   visit_miss_kerbopple = 8
   wash_covers          = 16
End Enum
Note: The values that are expressed in the series were defined peviously.

Assigning Values

As mentioned earlier, the unary or operator can concatenate values to create a result. For example, I can declare a todo item for Monday:

// C#
todo onMonday = ( walk_dog | cook_breakfast | wash_covers );
'' VB.NET
Dim onMonday As todo = ( walk_dog Or cook_breakfast Or wash_covers )

When this codeset is run, clr is actually evaluating the following:

00001 or 00100 or 10000.

The result of this operation would be 10101 (bitwise or will toggle a 1 if either or both binary digits in the sequence are true(1)). To put it another way:

0 0 0 0 1
0 0 1 0 0
1 0 0 0 0
1 0 1 0 1

For each digit, yield 1 where at least 1 in sequence is 1.

Say I have a variable of type todo that actually reflects what IS DONE.

todo is_done = do_nothing;
  1. For each item on the list, I concatenate the value of that item:
  2. // C#
    // assuming new_item is passed as a parameter and is of type todo.
    is_done = is_done | new_item;
    
    '' VB.Net
    '' assuming new_item is passed as a parameter and is of type todo.
    is_done = is_done Or new_item
    
  3. Because I forgot to walk the dog, he made a mess on my nice clean covers:
  4. // C#
    is_done = is_done | todo.wash_covers;
    is_done = is_done | todo.wash_covers;
    
    '' VB.Net
    is_done = is_done Or todo.wash_covers
    is_done = is_done Or todo.wash_covers
    
  5. The comparison would look like this:
  6. 10000 | 10000.
  7. The result would be 10000 because you are using a bitwise or, and 1 or 1 yields 1.

Testing Values

There are many means of testing to see whether (for the sake of this article) I washed the covers. Some are available online at MSDN, but I did not find those particularly useful. I am going to demonstrate the means that I found to be most appropriate.

Looking binary

test 1( 10000 is of 11000 ) test 2 (00100 is of 11000).

If I evaluate ( 10000 | 11000 ), I will get 11000. If I evaluate ( 00100 | 11000 ), I will get 11100.

This means that if I place the bitwise or between any two binary sequences, the result will be a binary sequence where each bit reflects whether at least one sequence is set to 1 in that exact position. If all of the 1s in sequence 1 are reflected as 1s in sequence 2 (as is the case in the first example), sequence 2's value will be the return value. The same applies in the reverse (all of sequence 2's 1s are reflected in sequence 1).

Expressed as flags

Consider:

// C#
todo for_monday = ( todo.walk_dog | todo.cook_breakfast |
                    todo.deliver_newspaper )
'' VB.Net
Dim for_monday As todo= ( todo.walk_dog Or todo.cook_breakfast Or todo.deliver_newspaper )

(for_monday | todo.wash_covers in c# ) (for_monday Or todo.wash_covers in VB.Net ) yields a different value. for_monday | todo.walk_dog yields for_monday. This is the case because, in appending the additional flags, no change was made. You now can express this in a function that will return true if the option has been completed.

// C#
bool testFlag( todo flg_test, todo option )
{
   /// do_nothing should not be compared in the same way (flag 0).
   if( option == todo.do_nothing )
   return false;
   // returns true if bitwise or yields same as flg_test
   // (no new binary digits have been set to 1).
   return flg_test == ( flg_test | option );
}
'' VB.Net
Function testFlag( todo flg_test, todo option ) As Boolean
   ''' do_nothing should not be compared in the same way (flag 0).
   if option = todo.do_nothing then return false
   '' returns true if bitwise or yields same as flg_test
   '' (no new binary digits have been set to 1).
   return (flg_test = ( flg_test Or option ))
End Function

Summary

Create a flags enum by using the flags attribute and specifying values in the series. You can assign multiple flags values to a variable typed as your flags enum datatype by using the bitwise or operator as shown:

myEnum newVal = ( myEnum.value1 | myEnum.value2 );

You can append additional flags using the same format:

newVal = (newVal | myEnum.value3);

You can use the bitwise or to test whether a flag is already set. The result will be equal to the variable you are testing if the flag is already set.



About the Author

David Oldfield

I still use windows XP.