Bitfields are a great way to maintain an arbitrary collection of bit flags using a single numeric value (usually an integer). For example, when using a System.Thread object in .NET you may want to use the ThreadState property. The property is of the type System.Threading.ThreadState which is an enum decorated with FlagsAttribute. The integer values of the enum are powers of 2 because we want to use the actual bits of the integer to mark different states (see previous post on .NET Integer Representation). Note that the in ThreadState states are not mutually exclusive so you can different states flagged at the same time.

[Flags]
public enum ThreadState
{
    Running = 0,
    StopRequested = 1,
    SuspendRequested = 2,
    Background = 4,
    Unstarted = 8,
    Stopped = 16,
    WaitSleepJoin = 32,
    Suspended = 64,
    AbortRequested = 128,
    Aborted = 256
}

With Int32 we have 32 bits that we can used. An integer indicating that the state is both WaitSleepJoin and AbortRequested (it can happen) will look like that:

Working with bitfields is all about bitwise operations. In order to assert whether the thread is in these two states and exacltly and these two states we can use the following:

t.ThreadState == (ThreadState.AbortRequested | ThreadState.WaitSleepJoin)

Checking if a bit is on:

(t.ThreadState & ThreadState.AbortRequested) == ThreadState.AbortRequested

Turning on a bit:

t.ThreadState |= ThreadState.AbortRequested

What happens if the FlagsAttribute is not used? Consider the following:

enum ColorsEnum
{
   None = 0,
   Red = 1,
   Green = 2,
   Blue = 4,
}
 
[Flags]
public enum ColorsFlaggedEnum
{
   None = 0,
   Red = 1,
   Green = 2,
   Blue = 4,
}

We have two enums with the same values and same names for the strings but one marked with the flags attribute and the other is not.

Console.WriteLine("Without Flags:");
for (int i = 0; i <= 8; i++)
{
    Console.WriteLine("{0,3}: {1}", i, ((ColorsEnum)i).ToString());
}
 
Console.WriteLine("With Flags:");
for (int i = 0; i <= 8; i++)
{
    Console.WriteLine("{0,3}: {1}", i, ((ColorsFlaggedEnum)i).ToString());
}

Running the code above will produce the following output:

Without Flags:
 0: None
 1: Red
 2: Green
 3: 3
 4: Blue
 5: 5
 6: 6
 7: 7
 8: 8
With Flags:
 0: None
 1: Red
 2: Green
 3: Red, Green
 4: Blue
 5: Red, Blue
 6: Green, Blue
 7: Red, Green, Blue
 8: 8

Seems that the FlagsAttribute does have some effect. But is it really necessary?

ColorsEnum y = ColorsEnum.Blue | ColorsEnum.Green;
Console.WriteLine(y.ToString());

Running this code will result in “6″ written on the console. Using dotPeek we have the following for Enum.ToString():

public override string ToString()
{
   return Enum.InternalFormat((RuntimeType) this.GetType(), this.GetValue());
}
 
private static string InternalFormat(RuntimeType eT, object value)
{
   if (!eT.IsDefined(typeof (FlagsAttribute), false))
      return Enum.GetName((Type) eT, value) ?? value.ToString();
   else
      return Enum.InternalFlagsFormat(eT, value);
}
 
private static string InternalFormat(RuntimeType eT, object value)
{
   if (!eT.IsDefined(typeof (FlagsAttribute), false))
      return Enum.GetName((Type) eT, value) ?? value.ToString();
   else
      return Enum.InternalFlagsFormat(eT, value);
}
 
private static string InternalFlagsFormat(RuntimeType eT, object value)
{
   ulong num1 = Enum.ToUInt64(value);
   Enum.HashEntry hashEntry = Enum.GetHashEntry(eT);
   string[] strArray = hashEntry.names;
   ulong[] numArray = hashEntry.values;
   int index = numArray.Length - 1;
   StringBuilder stringBuilder = new StringBuilder();
   bool flag = true;
   ulong num2 = num1;
   for (; index &gt;= 0 &amp;&amp; (index != 0 || (long) numArray[index] != 0L); --index)
   {
     if (((long) num1 &amp; (long) numArray[index]) == (long) numArray[index])
     {
        num1 -= numArray[index];
        if (!flag)
          stringBuilder.Insert(0, ", ");
        stringBuilder.Insert(0, strArray[index]);
        flag = false;
     }
   }
   if ((long) num1 != 0L)
      return value.ToString();
   if ((long) num2 != 0L)
      return ((object) stringBuilder).ToString();
   if (numArray.Length &gt; 0 &amp;&amp; (long) numArray[0] == 0L)
      return strArray[0];
   else
      return "0";
}

This is the route that a Flags decorated enum goes through when ToString is invoked. So the ToString implementation is sugar only, no actual meat involved. In fact one can use simple integers. The enum keyword is only sugar.

Types of Enums

An enum is not nesaccerally an integer, we can have any of the C# integral types except char. From the c# documentation:

The approved types for an enum are byte, sbyte, short, ushort, int, uint, long, or ulong.

http://msdn.microsoft.com/en-us/library/sbbt4032(v=vs.110).aspx

There is absolutely no need to use a signed integer, but it doesn’t hurt either if you work with bitwise operations. You should use only the number of bits required for your state. Use byte if you need 7 states or less. Use Int16 if you need 8-15 states and so on. The all-bits-off state is used for “None” state or something similar.

Performance

The best thing about bitfields is the performance gain compared with other naïve methods (several booleans, dictionary etc.). The increase in performance is twofold. First, memory access. If you use Booleans to store information about an object or any other information you will most likely want to assert the value of these bits in conjunction. An often overlooked optimization is the CPU cache. Assembly:

if (flag1 && flag2 && flag3)
0000011d cmp dword ptr [ebp-0Ch],0 
00000121 je 0000013A 
00000123 cmp dword ptr [ebp-10h],0 
00000127 je 0000013A 
00000129 cmp dword ptr [ebp-14h],0 
0000012d je 0000013A
{
Console.WriteLine("true");
0000012f mov ecx,dword ptr ds:[03662088h]
00000135 call 049BD3E4
}
 
Console.ReadLine();
0000013a call 04F84F64

When the CPU does a cmp operation, it will try to get it from the CPU cache and if it isn’t there it will go to machine memory and fetch it. When the data is not in the cache we experience a cache-miss. Thumb rule is 10 cycles for using data in the cache and 100 cycles from memory. If you are lucky (or extremely careful) all your bits will be in the same cache line. A cache line is the block the data is fetched in from the memory into the CPU cache. In the above example you might experience 3 cache-misses. If you are using a byte for example as your bit field, the entire state will be loaded into the cache and will require only one memory access. Modern CPUs have 32-64 byte cache lines. Assembly:

if (myColor == (ColorsEnum.Blue | ColorsEnum.Green))
000000f3 cmp dword ptr [ebp-0Ch],6 
000000f7 jne 00000104
{
Console.WriteLine("true");
000000f9 mov ecx,dword ptr ds:[032F2088h]
000000ff call 0485D3E4
}
 
Console.ReadLine();
00000104 call 04E24F64

Not only we have 2 instructions instead of 6, we do only one memory access. These numbers will hold for any number of bits and any combination of states the enum is in. ‘Nough said, bitfields rock.