Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Daniel Solis - Illustrated C# 2010 - 2010.pdf
Скачиваний:
20
Добавлен:
11.06.2015
Размер:
11.23 Mб
Скачать

CHAPTER 13 ENUMERATIONS

Bit Flags

Programmers have long used the different bits in a single word as a compact way of representing a set of on/off flags. Enums offer a convenient way to implement this.

The general steps are the following:

1.Determine how many bit flags you need, and choose an unsigned integral type with enough bits to hold them.

2.Determine what each bit position represents, and give it a name. Declare an enum of the chosen integral type, with each member represented by a bit position.

3.Use the bitwise OR operator to set the appropriate bits in a word holding the bit flags.

4.Unpack the bit flags by using the bitwise AND operator, or the HasFlag method.

For example, the following code shows the enum declaration representing the options for a card deck in a card game. The underlying type, uint, is more than sufficient to hold the four bit flags needed. Notice the following about the code:

The members have names that represent binary options.

Each option is represented by a particular bit position in the word. Bit positions hold either a 0 or a 1.

Since a bit flag represents a set of bits that are either on or off, you do not want to use 0 as a member value. It already has a meaning—that all the bit flags are off.

Hexadecimal representation is often used when working with bit patterns because there is a more direct correlation between a bit pattern and its hexadecimal representation than with its decimal representation.

Decorating the enum with the Flags attribute is not actually necessary but gives some additional convenience, which I’ll discuss shortly. Attributes are covered in Chapter 24.

[Flags]

enum CardDeckSettings : uint

{

SingleDeck

= 0x01,

// Bit 0

LargePictures

= 0x02,

// Bit 1

FancyNumbers

=

0x04,

//

Bit

2

Animation

=

0x08

//

Bit

3

}

Figure 13-4 illustrates this enumeration.

332

CHAPTER 13 ENUMERATIONS

Figure 13-4. Definition of the flag bits, and their individual representations

To create a word with the appropriate bit flags, declare a variable of the enum type, and use the bitwise OR operator to set the required bits. For example, the following code sets three of the four options:

Enum type

Flag word

 

Bit flags ORed together

 

 

CardDeckSettings ops = CardDeckSettings.SingleDeck

 

 

|

CardDeckSettings.FancyNumbers

 

 

|

CardDeckSettings.Animation ;

Prior to C# 4.0, to determine whether a particular bit was set, you would use the bitwise AND operator with the flag word and the bit flag.

For example, the following code checks a value to see whether the FancyNumbers bit flag is set. It does this by ANDing that value with the bit flag and then comparing that result with the bit flag. If the bit was set in the original value, then the result of the AND operation will have the same bit pattern as the bit flag.

bool useFancyNumbers =

(ops & CardDeckSettings.FancyNumbers) == CardDeckSettings.FancyNumbers;

 

Flag word

Bit flag

Figure 13-5 illustrates the process of creating the flag word and then checking whether a particular bit is set.

Figure 13-5. Producing a flag word and checking it for a particular bit flag

333

CHAPTER 13 ENUMERATIONS

This process of checking a flag word for a particular bit or set of bits is such a common task that C# 4.0 introduced a new instance method to the enum type to do the process for you. The method is called HasFlag. You use it on an instance of a flag word and pass it the bit flag you want to check for.

For example, the previous check for useFancyNumbers can be significantly shortened and simplified to the following statement:

UseFancyNumbers = ops.HasFlag(CardDeckSettings.FancyNumbers);

Flag word

Bit flag

The HasFlag method can also check for multiple bit flags. For example, the following code checks whether the flag word, ops, has both the Animation and FancyNumbers bits set. The code does the following:

The first statement creates a test word instance, called testFlags, with the Animation and

FancyNumbers bits set.

It then passes testFlags as the parameter to the HasFlags method.

HasFlags checks whether all the flags that are set in the test word are also set in the flag word, ops. If they are, then HasFlag returns true. Otherwise, it returns false.

CardDeckSettings testFlags =

CardDeckSettings.Animation | CardDeckSettings.FancyNumbers;

UseAnimationAndFancyNumbers = ops.HasFlag( testFlags );

Flag word

Test word

334

CHAPTER 13 ENUMERATIONS

The Flags Attribute

We’ll cover attributes in Chapter 24, but it’s worth mentioning the Flags attribute here. An attribute appears as a string between square brackets placed on the line above a class declaration. The Flags attribute does not change the calculations at all. It does, however, provide several convenient features.

First, it informs the compiler, object browsers, and other tools looking at the code that the members of the enum are meant to be combined as bit flags, rather than used only as separate values. This allows the browsers to interpret variables of the enum type more appropriately.

Second, it allows the ToString method of an enum to provide more appropriate formatting for the values of bit flags. The ToString method takes an enum value and compares it to the values of the constant members of the enum. If it matches one of the members, ToString returns the string name of the member.

Suppose, for example, that you have used the enum declaration for CardDeckSettings (given in the preceding code) and have not used the Flags attribute. The first line of the following code creates a variable (named ops) of the enum type and sets the value of a single flag bit. The second line uses ToString to get the string name of the member represented by that value.

CardDeckSettings ops = CardDeckSettings.FancyNumbers;

//

Set the bit flag.

Console.WriteLine( ops.ToString() );

//

Print its name.

This code produces the following output:

 

 

 

 

 

 

 

FancyNumbers

 

 

 

 

 

 

 

335

CHAPTER 13 ENUMERATIONS

That’s all well and good, but suppose you set two bit flags instead of one, as in the following code. Suppose also that you didn't use the Flags attribute on the enum declaration.

// Set two bit flags.

ops = CardDeckSettings.FancyNumbers | CardDeckSettings.Animation;

Console.WriteLine( ops.ToString() ); // Print what?

The resulting value of ops is 12, where 4 is from the FancyNumbers flag and 8 is from the Animation flag. In the second line, when ToString attempts to look up the value in the list of enum members, it finds that there is no member with the value 12—so it just returns the string representing 12. The resulting output is the following:

12

If, however, you change your code to use the Flags attribute before the declaration of the enum, this tells the ToString method that the bits can be considered separately. In looking up the value, it would find that 12 corresponds to the two bit flag members FancyNumbers and Animation. It would then return the string containing their names, separated by a comma and space, as shown here:

FancyNumbers, Animation

336

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]