Wednesday 21 December 2011

C# How To: Bit Flags

I use an Enumeration type whenever I need to define a set of named constants that can be assigned to a variable. Enums are easier on the brain than magic numbers and they ensure that valid values are being used throughout your code (you also have the added compile-time checking advantage).

The following enum models the months in the year:

enum Months { Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec }
To use the above enum, you define a variable of the enum type that can be assigned a value from the enumeration:

Months christmasMonth = Months.Dec;
The variable christmasMonth now represents the month December. It's worth knowing that by default, each enum value is assigned an integer value based on its order of definition (the underlying type of an enumeration value is actually an integer). In this example, the value Months.Jan is assigned the integer 0 and Months.Dec is assigned the int 11. These integer values can be overridden as we will see further below.

Sometimes, it makes sense to be able to assign multiple enum values to a single variable. For example, it would be nice to be able to assign multiple Month enum values to a Months variable called summerHolidayMonths. This is possible using the bit flag technique.

To create a bit flags enum, you assign the attribute [System.FlagsAttribute] to the enum definition and sequentially assign values of the power two (starting from zero) to each enumeration value:

[System.FlagsAttribute]
enum Months
{
     Jan = 0x0,   //000000000001 (base 2) 0 (base 10)
     Feb = 0x1,   //000000000010 (base 2) 1 (base 10)
     Mar = 0x2,   //000000000100 (base 2) 2 (base 10)
     Apr = 0x4,   //000000001000 (base 2) 4 (base 10)
     May = 0x8,   //000000010000 (base 2) 8 (base 10)
     Jun = 0x10,  //000000100000 (base 2) 16 (base 10)
     Jul = 0x20,  //000001000000 (base 2) 32 (base 10)
     Aug = 0x40,  //000010000000 (base 2) 64 (base 10)
     Sep = 0x80,  //000100000000 (base 2) 128 (base 10)
     Oct = 0x100, //001000000000 (base 2) 256 (base 10)
     Nov = 0x200, //010000000000 (base 2) 512 (base 10)
     Dec = 0x400  //100000000000 (base 2) 1024 (base 10)
}
Thus, the enum value Months.May, can now also be represented by the bit pattern "000000010000".

By thinking in terms of bits, we can now use the bitwise logical OR operator in C# to assign multiple enumeration values to a single enum variable, for example:

Months summerHolidayMonths = Months.May | Months.Jun | Months.Jul | Months.Aug;
summerHolidayMonths now represents the months May, June, July and August.

By logical OR'ing each value, we're storing the bit pattern "000011110000" in the summerHolidayMonths variable. As stated, this pattern was arrived at by bitwise OR'ing the bit patterns of each of the summer month enum values:

 Months.May = 000000010000
 Months.Jun = 000000100000
 Months.Jul = 000001000000
 Months.Aug = 000010000000
              ------------
Logical OR  = 000011110000


Clever... but how do we know if a particular month is stored in the summerHolidayMonths variable? We can use the bitwise logical AND operator like so:

bool isJuneInSummerMonths = (summerHolidayMonths & Months.June) == Months.June;
We can also remove a month from the summerHolidayMonths by computing the bitwise logical XOR of the variable with the enum value to remove. In the example below, we're removing the month May from summerHolidayMonths:

summerHolidayMonths = summerHolidayMonths ^ Months.May;
I'll leave it to you to apply the XOR on the months to see that it does really work!

Next time you define an enumeration, give some thought as to whether it makes sense to allow users of your enum to store multiple enumeration values in the enumeration variable - if it does make sense, then use bit flags!

No comments:

Post a Comment