Bit flags and bit flag manipulation is perhaps one of the most underrated strategies in programming, likely because most programmers developing in high-level languages rely too much on the high-level features, and forget about the low-level ones. Their role in programming, however, is still prominent, and likely will be for quite some time.
First of all, what are bit flags? A 'set' of flags is just a binary value, usually 8, 16, 32 or 64 bits long. They are usually expressed via enumerations, though I have seen cases where they are done strictly via hard-coded numeric values (which I don't advise doing). 32 bits is the default for .NET enumerations
. Obviously, everything we do is technically binary--numbers, letters, strings, everything, so a set of flags is no different than your average integer. The differences lies in how we visualize the integer, and how we treat it. The difference is in the intent, not in the actual implementation of details of the objects. Anything in the computer world is binary, and anything that is binary can be visualized by us, either into 0's and 1's, or a base-10 number (which is any number you usually encounter, such as 23, 45, 50, or what have you).
As you know, every value in any programming languages can be expressed in binary. The whole "0100101" thing we do is not actually binary--it's just how we, as humans, visualize binary. I'll be using that visualization throughout this article, and it will be geared towards C#. For this article, I'm going to assume you're familiar with how binary
works. I suggest you review that link if you cannot accurately convert numbers to and from binary until you can do so.
There are four main operators (common throughout most languages) that allow for useful manipulation of and 'interpretation' of these bits in no significant order:
- binary operator, also known as Bitwise OR.
- binary operator, also known as Bitwise AND.
- binary operator, also known as Bitwise XOR.
- unary operator, also known as Bitwise NOT, or the NOT operator.
First, I'll take an example directly from the .NET Framework--The FontStyle
enumeration. As you know, you can combine different styles to make one style for most fonts. For example, if you wanted a font to be both Bold and Italic, you would combine the Bold and Italic flags like so:
FontStyle boldItalic = FontStyle.Bold | FontStyle.Italic;
This is all well and great, but why
does it work? Let's take a look at the FontStyle enumeration, taken from Metadata via Visual Studio 2008 (cleaned up a bit for your benefit):
public enum FontStyle
Regular = 0,
Bold = 1,
Italic = 2,
Underline = 4,
Strikeout = 8
Notice a pattern yet? You should, as the values are dictated by the binary system. Each number is the result of taking 2 to the power of x, where x is incremented by 1, starting at 0. The first value, however, is 0, and (as you know), no exponent can make a number 0. This just means that all other values are turned 'off'. You can't very well have something Regular AND Italic at the same time, now can you? It would just be Italic.
Now let's look at the binary representations of these numbers:
00000000 = Regular
00000001 = Bold
00000010 = Italic
00000100 = Underline
00001000 = Strikeout
Notice a pattern now? I put the Strings on the right so the numbers would line up, by the way. You should notice that no two bits are on at the same time for each value, so each value is 'unique'.
So this is all well and great, but how do we actually use
this amazing knowledge? There four distinct operators that are particularily useful for manipulating these bits to your benefit:
Remember that Bitwise OR (|) compares corresponding bits, and if either bit is 1, it produces a 1 for that bit, too. With this knowledge, let's take a look at FontStyle.Bold (value of 1) OR'ed by FontStyle.Italic (value of 2):
In other words, BoldItalic has a value of 3. This value can't be confused with any other value, because no other value has this combination of bits. This also works for any combination you wish to use. Or, of course, you could express it like this (not advisable):
FontStyle boldItalic = (FontStyle)3;
Now let's assume it's your job to analyze a set of bits and tell which flags are on. For simplicitly's sake, we'll create a general method that checks if a specific flag is set on a FontStyle:
public static Boolean CheckIsOn(FontStyle style, FontStyle flags)
// Check if "flags" are on in the "style" set
There are a few ways to check if a flag is on (or off) in a set of flags, but I'll only explain one, as one is all you'll need. Bitwise AND does the same as Bitwise OR, except it returns a 1 only if both
corresponding bits are 1. So if we & style by flags, and the result is equal to "flags", we know that all the flags specified by "flags" are on. I'm saying "flags" in plural because we could pass a combined value as the parameter to "flags", so the following would be a valid call (given that someFontStyle is a properly declared FontStyle instance):
CheckIsOn(someFontStyle, FontStyle.Strikeout | FontStyle.Italic);
The implementation of the method could look like this:
return (style & flags) == flags;
You might be saying "well why don't we just & style by flags and check if the result is 0? If it isn't 0, obviously the flags are on!" Actually, no--this would only check if at least one of the flags specified in "flags" were on in the "style" parameter. Perhaps a seperate method would be more appropriate to check if any of the flags specified in the "flags" parameter are on in the "style" parameter:
public static Boolean CheckIsAnyOn(FontStyle style, FontStyle flags)
return (style & flags) != 0;
Now what happens if we want to turn a flag off for a certain set of flags? Surely we don't have to check each flag? Nope, this is where another operator comes into play, which is the Complement
operator. It's pretty basic--it just inverts all the bits in the value you provide. Unlike the other operators, it accepts only one operand. With this operator in hand, we can implement a TurnFlagsOff method:
public static FontStyle TurnFlagsOff(FontStyle style, FontStyle flags)
return (style & (~flags));
The implementation for the TurnFlagsOn method should already be apparent to you, if you'd like to implement one.
Now what about toggling a value on and off? Sure, you can check if it the flag is on, set it on if it isn't, and turn it off if it is, but this sounds like a big pain. That's why there's one more operator for us to use--the Exclusive OR (XOR)
operator. It does the same as the bitwise OR and bitwise AND operators, except it will produce a 1 only if one of the two operands is true, an the other is false. If both are false or both are true, it returns a 0. With this knowledge, we can implement a ToggleFlags method:
public static FontStyle ToggleFlags(FontStyle style, FontStyle flags)
return style ^ flags;
The last two operators that can prove useful in bit flag manipulation are the Left Shift
and Right Shift
operators. Explore these of your own accord, as their uses in this context are usually pretty rare, but they can be invaluable when you do find yourself in need of them.
The last piece of knowledge you'll need to start implementing your own flag manipulation techniques is how to create your own enum
in C#. Follow the pattern I mentioned above (sticking to powers of two), and remember to apply the FlagsAttribute
Attribute to the enumeration. Remember that you can use your enumerated values within your enumeration as public fields (technically, they are, anyway). You could implement a Cheese enumeration, if you really wanted to, like the following:
public enum Cheese
Any = 0,
American = 1,
Cheddar = 2,
Pepperjack = 4,
ColbyJack = 8,
Parmesian = 16,
// If you want, you can do combined values built in, too:
AmericanAndCheddar = American | Cheddar,
AmericanAndCheddarAndPepperjack = AmericanAndCheddar | Pepperjack,
All = American | Cheddar | Pepperjack | ColbyJack | Parmesian // etc...
// And on and on, until your fingers bleed... Though not required. Or recommended :P
Another, rather impressive article coded in C++ regarding bit flags can be found here at EE
I hope this has helped, and I hoped you enjoyed reading,