Common Type System—Flags-Style Enumerations
Flags-Style Enumerations
More often than not, values for an enum will be exclusive. That is, only one selection from the list of possible
values is valid for any single instance of the enum. Sometimes, however, programs must represent
a single instance as a combination of values. An enum type may be annotated with the System.FlagsAttribute custom attribute, causing languages that recognize this to permit combination and
extraction of individual values for a single instance. For example, consider a set of permissions for filebased
operations:
[Flags] enum FileAccess { Read = 1, Write = 2, ReadWrite = 3 }
It’s quite common to open a file for both Read and Write simultaneously. Using a flags-style enum
enables you to represent this idea. Notice the numeric value for Read and Write are power of two, starting
with 1. This is because combining or extracting values from a single instance is done using a bitwise
AND or OR. Additional values would occupy 4, 8, 16, 32, and so on. The C# compiler in particular doesn’t
auto-number these for you; you must do it manually; otherwise, you will end up with the default
sequential numbering scheme, which will not compose correctly with bitwise operations.
Notice that to represent the ability to Read and Write, the two independent values are ORed together. 1 |
2 is 3, hence the convenient value ReadWrite; it enables programmers to write the latter instead of the
former in this example, which is more convenient and readable:
FileAccess rw1 = FileAccess.Read | FileAccess.Write; // value '3' FileAccess rw2 = FileAccess.ReadWrite; // value '3'
With flags-style enum values, you obviously cannot test for equality to determine whether an instance
(with a combined set of values) contains a specific value. If you are testing a FileAccess instance for
Read access, the natural thing is to say this:
FileAccess fa = /*...*/; if (fa == FileAccess.Read) // permit read else // deny access
But unfortunately, if fa were FileAccess.ReadWrite, the test would fail and access would be denied.
In most cases, this is the wrong behavior. Instead, you must use a bitwise AND to extract individual values
from the instance:
FileAccess fa = /*...*/; if ((fa & FileAccess.Read) != 0) // permit read else // deny access
This check has the correct result, that is, that it the read check succeeds when a FileAccess.ReadWrite
is passed for the value.
|

