You have a
numeric value or an enumeration that contains bit flags. You need a
method to turn on (set the bit to 1
) or turn off
(set the bit to 0
) one or more of these bit flags.
In addition, you also want a method to flip one or more bit flag
values; that is, change the bit(s) to their opposite value.
The following method turns one or more bits on:
public static int TurnBitOn(int value, int bitToTurnOn) { return (value | bitToTurnOn); }
The following method turns one or more bits off:
public static int TurnBitOff(int value, int bitToTurnOff) { return (value & ~bitToTurnOff); }
The following method flips a bit to its opposite value:
public static int FlipBit(int value, int bitToFlip) { return (value ^ bitToFlip); }
When a large number of flags are required, and particularly when
combinations of flags can be set, it becomes cumbersome and unwieldy
to use Boolean variables. In this case, using the binary
representation of a number, we can assign each bit to indicate a
specific Boolean value. Each Boolean value is called a bit flag. For example, we have a number defined as a
byte
data type. This number is comprised of eight
binary bit values, which can be either a 1
or a
0
. Supposing we assign a color to each bit, our
number would be defined as follows:
byte colorValue = 0; // colorValue initialized to no color // colorValue Bit position // red 0 (least-significant bit) // green 1 // blue 2 // black 3 // grey 4 // silver 5 // olive 6 // teal 7 (most-significant bit)
By setting each bit to 0
or 1
,
we can define a color value for the colorValue
variable. Unfortunately, the colorValue
variable
does not take into account all colors. We can remedy this by allowing
multiple bits to be set to 1
. This trick allows us
to combine red (bit 0
) and green (bit
1
) to get the color yellow; red (bit
0
) and blue (bit 2
) to get
violet; or red, green, and blue to get white.
Note that we have used the byte
data type in
defining our colorValue
bitmask. This is because
it is more convenient to use unsigned data types for bit flag
variables. The other unsigned integers supported by C# are
ushort
, uint
, and
ulong
. This makes it easier to create the bitmask
values to use with the bit flag variable. Simply put, you do not have
to worry about negative values of the data type when using unsigned
data types.
Now that we have our bit flags set up in the
colorValue
variable, we need a way to set the
individual bits to a 0
or 1
, as
well as a way to determine whether one or more bits (colors) are
turned on. To do this, we use a
bitmask.
A bitmask is a constant number, usually of the same type as the
target type containing the bit flags. This bitmask value will be
AND
ed, OR
ed, or
XOR
ed with the number containing the bit flags to
determine the state of each bit flag or to set each bit flag to a
0
or 1
:
[Flags] public enum ColorBitMask { NoColorBitMask = 0, //binary value == 00000000 RedBitMask = 1, //binary value == 00000001 GreenBitMask = 2, //binary value == 00000010 BlueBitMask = 4, //binary value == 00000100 BlackBitMask = 8, //binary value == 00001000 GreyBitMask = 16, //binary value == 00010000 SilverBitMask = 32, //binary value == 00100000 OliveBitMask = 64, //binary value == 01000000 TealBitMask = 128, //binary value == 10000000 YellowBitMask = 3, //binary value == 00000011 VioletBitMask = 5, //binary value == 00000101 WhiteBitMask = 7, //binary value == 00000111 }
One common use for the
&
operator is to set one or more bits in a bit
flag value to 0
. If we AND
a
binary value with 1
, we always obtain the original
binary value. If, on the other hand, we AND
a
binary value with 0
, we always obtain
0
. Knowing this, we can use the bitmask values to
remove various colors from the colorValue
variable:
ColorBitMask color = YellowBitMask; ColorBitMask newColor = color & ~ColorBitMask.RedBitMask);
This operation removes the RedBitMask
from the
color
value. This value is then assigned to the
newColor
variable. The newColor
variable now contains the value 2
(00000010
in binary), which is equal to the
GreenBitMask
value. Essentially, we removed the
color red from the color yellow and ended up with the color green,
which is a constituent color of yellow.
The
|
operator can also be used to set one or more
bits to 1
. If we OR
a binary
value with 0
, we always obtain the original binary
value. If, on the other hand, we OR
a binary value
with 1
, we always obtain 1
.
Using this knowledge, we can use the bitmask values to add various
colors to the color
variable. For example:
ColorBitMask color = ColorBitMask.RedBitMask; ColorBitMask newColor = color | ColorBitMask.GreenBitMask;
This operation OR
s the
GreenBitMask
to the color
value, which is currently set to the value
RedBitMask
. This value is then assigned to the
newColor
variable. The newColor
variable now contains the value 3
(00000011
in binary); this value is equal to the
YellowBitMask
value. Essentially, we added the
color green to the color red and obtained the color yellow.
The
^
operator is often used to flip or invert one or
more bits in a bit flag value. It returns a 1
only
when either bit is set to 1
. If both bits are set
to 1
s or 0
s, this operator
returns a 0
. This operation provides a convenient
method of flipping a bit:
ColorBitMask color = ColorBitMask.RedBitMask; ColorBitMask newColor = color ^ ColorBitMask.RedBitMask;
The code shown here flips the least-significant bit (defined by the
RedBitMask
operation) to its opposite value. So if
the color were red, it would become 0
, or no
defined color, as shown here:
00000001 == Color (red)
^ 00000001 == RedBitMask
00000000
If we XOR
this result a second time with the
bitmask RedBitMask
, we get our original color
(red) back again, as shown here:
00000000 == Color (red)
^ 00000001 == RedBitMask
00000001 == red
If this operation is performed on the color yellow, we can obtain the color other than red that makes up this color. This operation is shown here along with the code:
ColorBitMask color = ColorBitMask.YellowBitMask;
ColorBitMask newColor = color ^ ColorBitMask.RedBitMask;
00000011 == Color (yellow)
^ 00000001 == RedBitMask
00000010 == green
See Recipe 1.4; see the “C# Operators” topic in the MSDN documentation.