Introduction
Although the technique could be considered outdated due to the price and amount of memory available on computers these days it is still in use within the .NET framework, as using bitwise operations with enumerations can be a rather useful way to pass multiple boolean settings to a single method.
What is an Integer?
An integer is mostly thought of as a number however there is another way to look at an integer. In memory an integer is represented by a 32 bit binary series of zeros and ones. For example the number 42 is represented in binary as 00101010.
| 128 |
64 |
32 |
16 |
8 |
4 |
2 |
1 |
| 0 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
32 + 8 + 2 = 42
However if you start to think of the zeros as false and the ones as true then the integer changes and becomes an array of 32 boolean values. This idea is the concept behind bitwise operations.
| Bit[7] |
Bit[6] |
Bit[5] |
Bit[4] |
Bit[3] |
Bit[2] |
Bit[1] |
Bit[0] |
| 128 |
64 |
32 |
16 |
8 |
4 |
2 |
1 |
| 0 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
For example you could have logic in your application that stated, if bit[5] == 1 then x is true, if bit[3] == 1 then y is true, and if bit[1] == 1 then z is true, and so on.
How do Enumerations fit in?
Enumerations make code more readable. I won't insult your intelligence as to why but I'd like to highlight the fact that the elements that make up enumeration are numeric values. This fact allows us to use an enumeration as a means to set an integer's value.
If we continue to think of an integer as an array of 32 boolean values then this means an enumeration allows us to switch one or more of the boolean values. If we're clever about the individual values of the elements that make up the enumeration then we can switch on a specific individual bit of the integer.
For example by giving the enumeration a value that maps to the two base binary then an enumerations can be used as a switch.
public enum Bitwise : int
{
Setting1 = 1, //0000 0001
Setting2 = 2, //0000 0010
Setting3 = 4, //0000 0100
Setting4 = 8, //0000 1000
Setting5 = 16, //0001 0000
Setting6 = 32, //0010 0000
Setting7 = 64, //0010 0000
Setting8 = 128, //0100 0000
Setting9 = 255, //1000 0000
};
If we use the enumeration Bitwise.Setting6 then bit[5], which represents 32, == 1 and hence, remembering the example above z is true.
With bitwise operations though we can go one further, by performing a bitwise OR we can combine the enumerations to set more than one boolean flag. For example using Bitwise.Setting6 and Bitwise.Setting2 we set two flags to true.
Before continuing the FlagsAttributeattribute can be used to specify that an enumeration should have its values set so that it can be used for bitwise operations. For example the above enumeration can be replaced with the following.
[System.Flags()]
public enum Bitwise2 : int
{
Setting1, //0000 0001
Setting2, //0000 0010
Setting3, //0000 0100
Setting4, //0000 1000
Setting5, //0001 0000
Setting6, //0010 0000
Setting7, //0010 0000
Setting8, //0100 0000
Setting9, //1000 0000
};
Bitwise ORs
To combine enumeration values so that they set multiple boolean flags you use bitwise ORs. Here is an example,
Bitwise settings = Bitwise.Setting2 | Bitwise.Setting4 | Bitwise.Setting8;
The term bitwise OR is a little bit confusing in my mind as the above line of code is easier to read if you think in terms of AND. For example, the Bitwise enumeration setting equals Setting2 and Setting4 and Setting8.
Which gives the enumeration setting an integer value of 42.
| Bit[7] |
Bit[6] |
Bit[5] |
Bit[4] |
Bit[3] |
Bit[2] |
Bit[1] |
Bit[0] |
| 128 |
64 |
32 |
16 |
8 |
4 |
2 |
1 |
| 0 |
0 |
1 |
0 |
1 |
0 |
1 |
0 |
| Setting8 |
Setting7 |
Setting6 |
Setting5 |
Setting4 |
Setting3 |
Setting2 |
Setting1 |
Accessing the bits of an Integer
There are three things you will need to do with the bits of an integer.
- Get the value of a bit
- Set the value of a bit to 1
- Set the value of a bit to 0
Unfortunately the code to do these three tasks is pretty nasty. For example, to get the value of the first bit of an integer named myIntValue you would use the following code.
myIntValue & (1 << 0)
Not exactly the most understandable piece of code you'll ever comes across and setting the values of individual bits is just as bad, however there is a rather nicer approach you can take that helps keep things cleaner.
The approach involves creating a wrapper for the integer in which an indexer has been created that lets you access the integer as an array of bits and does so in such a way that you can set myIntValue[0] = false, or form logical statements using code like 'if (myIntValue[3] == true)'.
Here is an example of this wrapper, it demonstrates the code that's used to set individual bits of an integer on or off.
public struct IntAsBits
{
int settings;
public IntAsBits(Bitwise settings)
{
this.settings = (int)settings;
}
public bool this[int index]
{
get
{ //check the bit at index and return true or false
return (settings & (1 << index)) != 0;
}
set
{
if (value)
settings |= (1 << index); //turns the bit at index on
else
settings &= ~(1 << index); //turns the bit at index off
}
}
}
This would be used similar to this:
static void Main(string[] args)
{
Bitwise settings = Bitwise.Setting2 | Bitwise.Setting4 | Bitwise.Setting8;
IntAsBits bits = new IntAsBits(settings);
bits.DisplaySettings();
Console.ReadLine();
}
Which would produce an output similar to below. This demonstrates that by passing a bitwise OR'd enumeration into a method or class gives you the ability to use 32 different boolean settings within that method or class.

Word of Warning
This old school approach to memory conservation is rather handy but I need to point out that many new age developers might not know this approach. If you use it in your projects then make sure you fully document what you doing so someone else can pick up the project later.
Summary
Bitwise operations involve treating the individual bits of an integer as an array of 32 boolean flags. By combining this concept with enumerations and bitwise OR's you can enable and disable particular bits in a much more readable fashion and by wrapping an integer in a custom class that provides an indexer you can simplify access to the individual bits and access them as a boolean value. You need to consider though if you really need to take this approach and if you do make sure you document it as it's a somewhat forgotten art.
About Derek Smyth
 |
Sorry, no bio is available
This author has published 6 articles on DotNetSlackers. View other articles or the complete profile here.
|
You might also be interested in the following related blog posts
Bounded blocking queues
read more
TSql vs. SQL CLR Performance Analysis
read more
|
|
Please login to rate or to leave a comment.