Using Enumerated Members in a Bit Mask






Using Enumerated Members in a Bit Mask

Problem

An enumeration of values is needed to act as bit flags that can be ORed together to create a combination of values (flags) in the enumeration.

Solution

Mark the enumeration with the Flags attribute:

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

Combining elements of this enumeration is a simple matter of using the bitwise OR operator (|). For example:

	Language lang = Language.CSharp | Language.VBNET;

Discussion

Adding the Flags attribute to an enumeration marks this enumeration as individual bit flags that can be ORed together. Using an enumeration of flags is no different than using a regular enumeration type. It should be noted that failing to mark an enumeration with the Flags attribute will not generate an exception or a compiletime error, even if the enumeration values are used as bit flags.

The addition of the Flags attribute provides you with two benefits. First, if the Flags attribute is placed on an enumeration, the ToString and ToString("G") methods return a string consisting of the name of the constant(s) separated by commas. Otherwise, these two methods return the numeric representation of the enumeration value. Note that the ToString("F") method returns a string consisting of the name of the constant(s) separated by commas, regardless of whether this enumeration is marked with the Flags attribute. For an indication of why this works in this manner, see the "F" formatting type in Figure in Recipe 1.16.

The second benefit is that when you examine the code and encounter an enumeration, you can better determine the developer's intention for this enumeration. If the developer explicitly defined this as containing bit flags (with the Flags attribute), you can use it as such.

An enumeration tagged with the Flags attribute can be viewed as a single value or as one or more values combined into a single enumeration value. If you need to accept multiple languages at a single time, you can write the following code:

	Language lang = Language.CSharp | Language.VBNET; 

The variable lang is now equal to the bit values of the two enumeration values ORed together. These values ORed together will equal 3, as shown here:

	Language.CSharp 0001
	Language.VBNET  0010
	ORed bit values 0011

The enumeration values were converted to binary and ORed together to get the binary value 0011 or 3 in base10. The compiler views this value both as two individual enumeration values (Language.CSharp and Language.VBNET) ORed together or as a single value (3).

To determine if a single flag has been turned on in an enumeration variable, use the bitwise AND (&) operator, as follows:

	    Language lang = Language.CSharp | Language.VBNET;

	    if((lang & Language.CSharp) == Language.CSharp)
	    Console.WriteLine("The enum contains the C# enumeration value");
	else
	    Console.WriteLine("The enum does NOT contain the C# value");

This code will display the text "The enum contains the C# enumeration value." The ANDing of these two values either will produce zero if the variable lang does not contain the value Language.CSharp, or it will produce the value Language.CSharp if lang contains this enumeration value. Basically, ANDing these two values looks like this in binary:

	Language.CSharp | Language.VBNET 0011
	Language.CSharp                  0001
	ANDed bit values                 0001

This is dealt with in more detail in Recipe 1.21.

In some cases the enumeration can grow quite large. You can add many other languages to this enumeration, as shown here:

	[Flags]
	enum Language
	{
	    CSharp = 0x0001, VBNET = 0x0002, VB6 = 0x0004, Cpp = 0x0008,
	    FortranNET = 0x0010, JSharp = 0x0020, MSIL = 0x0080
	}

When a Language enumeration value is needed to represent all languages, you would have to OR together each value of this enumeration:

	Language lang = Language.CSharp | Language.VBNET | Language.VB6 ;

Instead of doing this, you can simply add a new value to this enumeration that includes all languages as follows:

	[Flags]
	enum Language
	{
	    CSharp = 0x0001, VBNET = 0x0002, VB6 = 0x0004, Cpp = 0x0008,
	    FortranNET = 0x0010, JSharp = 0x0020, MSIL = 0x0080,
	    All = (CSharp | VBNET | VB6 | Cpp | FortranNET | JSharp | MSIL)
	} 

Now there is a single enumeration value, All, that encompasses every value of this enumeration. Notice that there are two methods of creating the All enumeration value. The second method is much easier to read. Regardless of which method you use, if individual language elements of the enumeration are added or deleted, you will have to modify the All value accordingly.

Similarly, you can also add values to capture specific subsets of enumeration values as follows:

	[Flags]
	enum Language
	{
	    CSharp = 0x0001, VBNET = 0x0002, VB6 = 0x0004, Cpp = 0x0008,
	    CobolNET = 0x000F, FortranNET = 0x0010, JSharp = 0x0020,
	    MSIL = 0x0080,
	    All = (CSharp | VBNET | VB6 | Cpp | FortranNET | Jsharp | MSIL),
	    VBOnly = (VBNET | VB6),
	    NonVB = (CSharp | Cpp | FortranNET | Jsharp | MSIL)
	}

Now you have two extra members in the enumerations, one that encompasses VB-only languages (Languages.VBNET and Languages.VB6) and one that encompasses non-VB languages.



 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows