Common Type System—Flags-Style Enumerations


Jump to: navigation, search
Visual C# Tutorials
.NET Framework Tutorials

Common Type System

© 2006 Wiley Publishing Inc.

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.


Previous_Page_.gif Next_Page_.gif





Personal tools