Wednesday 12 May 2010

Bit Field Enumerations

I learned a neat trick today which allows you to store multiple values in a single database field by linking it to an enumeration.

The programming construct behind the idea is called a Bit Field. It's simple, really. An enumeration is basically just a list of numeric values linked to text labels, and the usual form is to increase the numeric values incrementally, so ...

Public Enum ColoursEnum
Clear = 0
Red = 1
Blue = 2
Green = 3
Yellow = 4
Purple = 5
End Enum

But what if, instead of assigning your numeric values incrementally you decide to double them up each time, creating a number series which can be represeted by a bit:

Public Enum ColoursEnum
Clear = 0
Red = 1
Blue = 2
Green = 4
Yellow = 8
Purple = 16
End Enum

Looks a bit odd, no? Well whilst it does *look* odd you can do something clever with this series of values: the gap between each one means that if you wanted to, say, have an Enum with more than one value each potential value has a unique numeric identifier created by adding the values together. A numeric value of 12, in the above example, can only be made up by adding Green and Yellow. A numeric value of 19 can only be reached by adding Red, Blue and Purple. A numeric value of 2 can only be reached if the Enum is Blue and Blue alone.

So if you create a single database field to hold a single int, by making it a bitwise field you can make it hold more than one value. This has a multitude of uses: I came across it whilst trying to create a "status" column for a database object that could in fact have more than one status at once. Rather than the ugly approach of creating a boolean column for each potential status, I can have a single status column, link it to an Enum and have a bit field join to make it hold multiple values. Neat!

Be aware that in such an enumeration the "zero" value has to be unique - it can't be a part of any other combination of numbers. So you need to assign zero a value and it needs to be some sort of "resting" value indicating the enumeration is effectively empty - even if that means linking it to a text flag like "empty" or "not set".

However you may think you've spotted a flaw here: am I not going to have to introduce some slightly annoying and convoluted logic somewhere to take the value from the database, look at the enumeration and decide what set of values my field is actually holding? Thankfully the .net framework automates all of this for us.

All you have to do is add a Flags attribute to the enum:

_
Public Enum ColoursEnum
Clear = 0
Red = 1
Blue = 2
Green = 4
Yellow = 8
Purple = 16
End Enum

And .Net does all the work for you. It's slightly different in C# by the look of things: you need to use [FlagsAttribute] instead of but the end result is the same.

.Net will now treat each potential set of values from this enumeration differently. That is to say that if you loop through all the potential values of Colours above you'll get separate entries for Red, RedBlue, RedBlueGreen, RedGreen and so on.

You can also set your Enum to hold multiple values by using a bitwise "OR" operator (a pipe |):

Dim myRed as ColoursEnum = ColoursEnum.Red
Dim myBrown As ColoursEnum = ColoursEnum.Red | ColoursEnum.Purple

And in the same manner you can use a bitwise "AND" operator (an ampersand &) to isolate a single value and check it. Colours are a bad way of demonstrating this but hopefully the logic will read through.

Dim myBrown As ColoursEnum = ColoursEnum.Red | ColoursEnum.Purple

Dim myRed As ColoursEnum = myBrown & ColoursEnum.Red
'This will set MyRed equal to Red, since it matches something in the enum
Dim noColour As ColoursEnum = myBrown & ColoursEnum.Green
'This will set noColour equal to Clear since green doesn't match anything in the enum