An
enumeration of values is needed to act as bit flags that can be
OR
ed together to create a combination of values
(flags) in the enumeration.
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;
Adding the Flags
attribute to an enumeration marks
this enumeration as individual bit flags that can potentially be
OR
ed 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
would not generate an exception or a compile-time error even if the
enumeration were used as bit flags.
The addition of the Flags
attribute provides you
with three 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 Table 4-1 in Recipe 4.1.
The second benefit is that when you are using reflection to traverse
code and encounter an enumeration, you are able to determine the
developer’s intention for this enumeration. If the
developer explicitly defined this enumeration as containing bit flags
(with the Flags
attribute), you can use it as
such.
The third benefit is similar to the second benefit. If a developer
marks an enumeration with the Flags
attribute, you
know that the developer intended this enumeration to contain only bit
flags. This is similar to documenting one’s code.
Knowing that this enumeration can be used as a bitmask, you can more
easily determine what is going on within this
developer’s code.
The flags enumeration 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 could 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 OR
ed together. These
values OR
ed together will equal three, as shown
here:
Language.CSharp 0001
Language.VBNET 0010
ORed bit values 0011
The enumeration values were converted to binary and
OR
ed 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
) OR
ed together
or as a single value (3
).
To determine whether a single flag has been turned on in an
enumeration variable, we need to 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
AND
ing of these two values will produce either
zero, if the variable lang
does not contain the
value Language.CSharp
; or the value
Language.CSharp
, if lang
contains this enumeration value. Basically, AND
ing
these two values looks like this in binary:
Language.CSharp | Language.VBNET 0011
Language.CSharp 0001
ANDed bit values 0001
This case is dealt with in more detail in Recipe 4.6.
In some cases, the enumeration can grow quite large. We could 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 }
In the cases where a Language
enumeration value is
needed to represent all languages, we would have to
OR
together each value of this enumeration:
Language lang = CSharp | VBNET | VB6 | Cpp | FortranNET | Jsharp | MSIL
Instead of doing this, we 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 = 0xFFFF }
or:
[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; however, if individual
language elements of the enumeration are added or deleted, you will
have to modify the All
value accordingly.
Similarly, we could also add values to capture specific subsets of enumeration values as follows:
[Flags] enum Language { CSharp = 0x0001, VBNET = 0x0002, VB6 = 0x0004, Cpp = 0x0008, FortranNET = 0x0010, JSharp = 0x0020, MSIL = 0x0080, All = 0x00FF, VBOnly = 0x0006, NonVB = 0x00F9 }
or:
[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) VBOnly = (VBNET | VB6), NonVB = (CSharp | Cpp | FortranNET | Jsharp | MSIL) }
Now we 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.
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.