Tuesday 5 October 2010

Using bitwise operators with enum in C#

Enumerations, or enums for short, are not something we tend to use every day. But they do have their place, and can often be used to solve unusual problems elegantly, while making your code easier to understand.

One area in which they are really handy, is where you need to set a property of some object, which itself can have one or many values. One application I am working on now uses a provider model back-end to store xml data in a database. I wanted to increase the robstness and flexibility of this application, by allowing the data to come from either source - eg: in case the database server goes down. To add to the problem, I had created triggers to prune old data from various database tables. Basically, I have a Data table, and another 3 tables containing statistics about the data, by day, month and year. The Data and DaysStats tables are trimmed after 1 year, the MonthsStats table after 3 years, and the YearsStats table after 5 years. So depending on the date requested by the application, the data layer needs to know where it can get the data from. An Enum is at the heart of how I handle this in my data layer. I'm going to show it to you, then explain it:

    [Flags]
    private enum DataSource : byte
    {
        Xml = 1,
        SqlDays = 4,
        SqlMonths = 8,
        SqlYears = 16,
        All = Xml | SqlDays | SqlMonths | SqlYears
    }

The [Flags] attribute will allow us to do bitwise combinations of the DataSource values. You can see this in the "All" value - this is basically for convenience in my code, and is equivalent to writing "Xml | SqlDays | SqlMonths | SqlYears".

Before I go on, let me explain bitwise logic. You see, the values in this enum are stored as bytes like so:

Xml = 1 (0x00000001)
SqlDays = 2 (0x00000010)
SqlMonths = 4 (0x 00000100)
SqlYears = 8 <0x00001000)

When I do the bitwise OR operation to get All, this does:
00000001
OR
00000010
OR
00000100
OR
00001000

which equates to 00001111 (Binary OR Operation)

Now lets say in my data access layer, I want to access data from 1 year and 3 months ago. My code would automatically set my instance of DataSource like so:

m_dataSrc |= DataSource.SqlMonths | DataSource.SqlYears | DataSource.Xml
which is just a shortcut for:
m_dataSrc = m_dataSrc | DataSource.SqlMonths | DataSource.SqlYears | DataSource.Xml

When working with a bitwise enums, use bitwise ORs to set values, but bitwise ANDs to check values. For example:

if((m_dataSrc & SummaryDataSource.SqlDays) > 0)
     // Get data from database
else
    // Get data from xml files

That's really all there is to it. Bitwise enums are really easy to use and can save you many lines of code and hours of frustration to give you an elegant solution to many problems.

No comments: