James Michael Hare

...hare-brained ideas from the realm of software development...
posts - 166 , comments - 1483 , trackbacks - 0

My Links

News

Welcome to my blog! I'm a Sr. Software Development Engineer in the Seattle area, who has been performing C++/C#/Java development for over 20 years, but have definitely learned that there is always more to learn!

All thoughts and opinions expressed in my blog and my comments are my own and do not represent the thoughts of my employer.

Blogs I Read

Follow BlkRabbitCoder on Twitter

Tag Cloud

Archives

Post Categories

.NET

CSharp

Little Wonders

Little Wonders

vNext

C# Fundamentals: Combining Enum Values with Bit-Flags

Two posts ago, I talked about the C# enum and some of its pitfalls (here).  This post continues with a discussion of the fundamentals of enums by continuing with using enums and bit-flags.

Defining a [Flags] Enum

Now, we’ve seen previously that enums are typically used when you want to represent a type that can be one of a distinct set of values.  But they can also be used to store a combination of discrete values. 

That is, the standard use of an enumeration is to support mutex options - such as an order can be a New or Modify but not both. But it is not limited to that, you can form a value that is a combination of enumeration values if the values are structured in the right way - for example, a file can be opened for Read access, Write access, or both.

For example, what if we created an enum like this:

   1: [Flags]
   2: public enum MessagingProperties
   3: {
   4:          // No options selected (value is 0)
   5:          None = 0,
   6:          // messages are not discarded if subscriber is slow (value is 1)
   7:          Durable = 1,
   8:          // messages are saved to disk in case messaging crashes (value is 2)
   9:          Persistent = 2,
  10:          // messages are buffered at send/receive point so not blocking (value is 4)
  11:          Buffered = 4,
  12: }

You may notice we decorated our enum with a [Flags] attribute.  You may also notice we skipped the value of 3 in our enumeration, we’ll get to each of these points in turn in the sections that follow,  for now take it on faith that these steps are best practices and you'll soon see the reason.

So now, this definition of a [Flags] enum will allow us to have a MessagingProperties value that is both Durable and Persistent, or is both Buffered and Durable, or is all three, or is none of the three, and so on and so on.  But how would we represent this in code? 

We do this by combining the flags using bitwise logic (those of you who come from C++ should feel right at home here) using the OR (‘|’) operator.  Note that we use the bitwise OR ('|') and not the logical OR ('||').  Attempting to use the logical operators to perform bitwise logic results in a compile time error (and if this were C++ it wouldn't do what you wanted anyway), so always use the bitwise operators when combining and testing enum values.

Combining Bitwise Values

Remember that the binary OR operator will combine the bits of both numbers such that the resulting bit is 1 if either source bit is 1.  Otherwise it’s zero:

   1: 1000 01001
   2: 0010 01011
   3: ---------- OR
   4: 1010 01011

Notice that in the example above, every place where either operand had a 1 bit, the result bit is also 1.  The only place where the result is a 0 bit is where both source bits were 0.

This is also why we use the binary OR instead of addition.  If we were to do addition (+) of the two binary values, it is true we would get 1 where only one of the operand bits was 1, but if they were BOTH 1, we’d result in 0 and carry a 1 to the next bit:

   1: 1000 01001
   2: 0010 01011
   3: ---------- ADD
   4: 1010 10100

Notice the results are not the same!  Thus if you try to use addition (+) instead of the bitwise OR (|), if the bit is already set, you won’t keep it, but you’ll potentially set or unset the next higher bits.  This is why we always use bitwise OR for safety. 

So armed with this knowledge, we can try to combine elements in our enum:

   1: MessagingProperties result = MessagingProperties.Durable | MessagingProperties.Persistent; 

Now, you’ve set the 1s bit and the 2s bit because the default value of Durable is 1 and the default value of Persistent is 3 due to their position in the enum and the fact we haven’t overridden our values.  So now, if we combine them using OR like above, we get:         

   1: 0000 0001 (binary representation of 1)         
   2: 0000 0010 (binary representation of 2)
   3: --------- OR 
   4: 0000 0011 (result of 3)

Thus, both bits have been set for each value.  It might be tempting to think of this as an AND instead of an OR, but you must keep in mind the binary math.  Combining in binary math is an OR operation, not an AND operation.  Let’s look, if we had tried to use AND:

   1: // wrong, use | not & to combine
   2: MessagingProperties result = MessagingProperties.Durable & MessagingProperties.Persistent; 

Then we would have gotten:

   1: 0000 0001 (binary representation of 1)         
   2: 0000 0010 (binary representation of 2)
   3: --------- AND      
   4: 0000 0000 (result of 0)

Whoa!  Zero?  Yes, remember that the bitwise and sets a resulting bit to 1 only if BOTH source bits are 1.  So combining our bits with AND isn’t an option.

Default Enumeration Values Are Not Appropriate for Bitwise Enums

Remember I said that the default numbering of enum values was to start with 0 and increase by one for each value?  Well, this is death for a bit-flags enum that has more than 3 values!  First of all, your first value is always zero by default.  What if we had:

   1: [Flags]
   2: public enum ShapeProperties
   3: {                 
   4:     Fill,
   5:     Outline,
   6:     Shadow,
   7:     Dither,
   8: }

 
Well now we have a problem because it’s impossible to set the Fill flag because it’s value is 0, and anything ORd with 0 is itself.  Thus, with bitwise enums you should either start at 1, or define a None value with value zero.

Another problem is the default incrementation of the values.  Remember that Dither will default to 3.  Well, what is 3 in binary?

   1: 0000 0011 (binary representation of 3)

Notice that the binary 3 is the combination of the binary 1 and binary 2 (1s and 2s bits both set).  This means that if we used this enumeration as is, ShapeOptions.Dither would be equal to ShapeOptions.Outline | ShapeOptions.Shadow which is not what we want at all.  We may think we’re setting Dither, but in reality it will interpret this as being Outline and Shadow.

This is why we must make sure our values are bit-unique.  That is, the integer value of each enumerated value must be represented by a single, unique bit.  The easiest way to do this is by assigning powers of two (you can use decimal or hexadecimal if you prefer).  That’s why our MessagingProperties defined at first didn’t accept the default values in our first code snippet.  Also, if we wanted, we could have defined the values using hex:

   1: [Flags]
   2: public enum MessagingProperties
   3: {
   4:          // No options selected (value is 0)
   5:          None = 0x00,
   6:          // messages are not discarded if subscriber is slow (value is 1)
   7:          Durable = 0x01,
   8:          // messages are saved to disk in case messaging crashes (value is 2)
   9:          Persistent = 0x02,
  10:          // messages are buffered at send/receive point so not blocking (value is 4)
  11:          Buffered = 0x04
  12: }

As long as we stick with powers of two we are guaranteed each value will be a single, unique bit.  This does create a little more work on our end to guarantee that the bits are unique when we add new values.

Now when we look at Buffered in binary, it is no longer the combination of Durable ORd with Persistent:

   1: Durable | Persistent:
   2:  
   3: 0000 0001            (Durable = 1)
   4: 0000 0010            (Persistent = 2)
   5: --------- OR
   6: 0000 0011            (result = 3)
   7:  
   8:  
   9: Versus Buffered:
  10:  
  11: 0000 0100            (Buffered = 4)

In fact, if we combine them all, we can see that all the bits fit together nicely and are unique:

   1: MessagingProperties result = MessagingProperties.Durable | MessagingProperties.Persistent | 
   2:                 MessagingProperties.Buffered;

Results in:

   1: 0000 0001            (Durable = 1)
   2: 0000 0010            (Persistent = 2)
   3: 0000 0100            (Buffered = 4)
   4: --------- OR                
   5: 0000 0111            (result = 7)

 

Getting Values Back Out of Bit-flags Enum

So how do we get values back out of a bit-flags enum?  Well, there’s two ways to do this, we can either use the new C# 4 Enum.HasFlag() method, or we can use a binary AND (&) to test the bits.

First, let’s look at the old-school bitwise AND.  Let’s say you wanted to write a method to see if a MessagingProperties value had any of the flags set:

   1: public static void TestBits(MessagingProperties value)
   2: {
   3:         if ((value & MessagingProperties.Buffered) != 0)
   4:         {
   5:                  Console.WriteLine("Buffered.");
   6:         }
   7:         if ((value & MessagingProperties.Persistent) != 0)
   8:         {
   9:                  Console.WriteLine("Persistent");
  10:         }
  11:         if ((value & MessagingProperties.Durable) != 0)
  12:         {
  13:                  Console.WriteLine("Durable");
  14:         }
  15: } 

Notice that when testing using the old bitwise AND method (&) we AND the value we are testing against the flag value we want to check, and compare for a non-zero result.   There are other ways to do this, of course, but this is the most typical.  This is because remember that in a bitwise AND, the resulting bits are only set if both source bits are binary 1s.  Let’s assume that Durable and Persistent are set:

   1: MessagingProperties result = MessagingProperties.Durable | MessagingProperties.Persistent; 

If we compare for Durable, we get:

   1: 0000 0011            (result = Durable | Persistent)
   2: 0000 0001            (Durable)
   3: --------- AND
   4: 0000 0001            1 bit set which is != 0 therefore Durable is set.

Likewise if we compare for Buffered, we should get zero:

   1: 0000 0011            (result = Durable | Persistent)
   2: 0000 0100            (Buffered)
   3: --------- AND
   4: 0000 0000            No bit set which is == 0 therefore Buffered is not set.

So if we run result through this function, we will get:

   1: Persistent
   2: Durable

Which is what we expect.  So that’s using the old C++-ish bitwise logic for detecting bits.  It so happens that .Net 4.0 has blessed us with a much easier-to-read method of checking enum flags, the Enum.HasFlag()method.  We could re-write our flag checker to the following:

   1: public static void TestBits(MessagingProperties value)
   2: {
   3:         if (value.HasFlag(MessagingProperties.Buffered))
   4:         {
   5:                  Console.WriteLine("Buffered.");
   6:         }
   7:         if (value.HasFlag(MessagingProperties.Persistent))
   8:         {
   9:                  Console.WriteLine("Persistent");
  10:         }
  11:         if (value.HasFlag(MessagingProperties.Durable))
  12:         {
  13:                  Console.WriteLine("Durable");
  14:         }
  15: } 

Now the code is much easier to read and more meaningful.

The Curious FlagsAttribute

You probably noticed the [Flags] attribute applied to our enum when we started talking about bit-flags.  This allows for some methods to work better with bit-flags in our enums.  You may be surprised, though, to know that everything we’ve done so far on this blog entry can be done without it!  In fact, the only thing the [Flags] attribute does is to allow the Enum.ToString() method to print multiple flags for a bit-flag value.

Here’s the breakdown:

  • Only works with [Flags]:
    • Allow ToString() to print comma-separated list of enum flag values.
  • Works regardless of whether [Flags] is used or not:
    • Enum.Parse() will handle comma-separated list of values and combine them.
    • Bit-wise math works (will not even warn you if no [Flags] – though Resharper does).
  • What [Flags] does not do:
    • enum will not automatically assign powers-of-two, always increments by 1 by default.

So the only functionality that adding [Flags] does is give you a better ToString().  Don’t get me wrong, I’m not saying ignore the [Flags] attribute, but I am saying that you have to be mindful of what it doesn’t do.  The [Flags] attribute does give you a better ToString() after all and it gives you a good semantic aid that shows the intention of the enum which makes your code better documented and easier to maintain.

Technorati Tags: ,,

 

 Technorati Tags: , , , ,

 

Print | posted on Thursday, July 22, 2010 5:46 PM | Filed Under [ My Blog C# Software .NET Fundamentals ]

Feedback

Gravatar

# re: C# Fundamentals: Combining Enum Values with Bit-Flags

Hmm, Enum.HasFlag()?
7/23/2010 4:00 AM | Sparkie
Gravatar

# re: C# Fundamentals: Combining Enum Values with Bit-Flags

@Sparkie: yes, .net 4.0 adds the HasFlag method to System.Enum so you can call it off of any enumeration instance:

MyEnum value = ...;
if (value.HasFlag(MyEnum.SomeFlag))
...
7/23/2010 6:34 AM | James Michael Hare
Gravatar

# re: C# Fundamentals: Combining Enum Values with Bit-Flags

One thing to watch out for when using the HasFlag method is that you shouldn't check for your None (0) value using it, it will always return true. (This may already be obvious to you, since you didn't include a check for it in your TestBits method).

That is, if you say var whatever = MessagingProperties.Durable | MessagingProperties.Buffered;

despite what you might think, the way HasFlag is implemented, whatever.HasFlag(MessagingProperties.None) will return true.
7/26/2010 9:56 AM | Jordan
Gravatar

# re: C# Fundamentals: Combining Enum Values with Bit-Flags

@Jordan:

Very good point. Yes, because HasFlag checks for (value & flag) == flag (instead of != 0), it would always return true if flag == 0.
7/27/2010 3:52 PM | James Michael Hare
Gravatar

# re: C# Fundamentals: Combining Enum Values with Bit-Flags

Thanks for the great post. Sometimes when you don't touch some of these basic things for a long time, a little refresher is great.
11/5/2010 4:12 PM | Jeff Martin
Gravatar

# re: C# Fundamentals: Combining Enum Values with Bit-Flags

@Jeff:

Very true... always good to remind yourself what's there! Atrophy can be a nasty thing...
11/7/2010 10:24 AM | James Michael Hare
Gravatar

# re: C# Fundamentals: Combining Enum Values with Bit-Flags

This is one of the best answer so far, I have read online. Just useful information. Very well presented. Thanks for sharing with us. I had found another nice post with wonderful explanation on Enumeration in c#, which is also helped me to complete my task. For more details of that post check out this link....

http://mindstick.com/Articles/ade257fc-7058-4f60-a0fe-85c7ca52f004/?Enumeration%20in%20c#

Thanks everyone for your precious post.
1/9/2012 5:04 AM | Rajesh Singh
Gravatar

# re: C# Fundamentals: Combining Enum Values with Bit-Flags

Just what I needed to point one of my new developers towards. Thanks!

It's surprising how little this stuff is covered in your typical programming course these days.
1/16/2013 9:41 AM | Brian
Gravatar

# re: C# Fundamentals: Combining Enum Values with Bit-Flags

Also look
http://msdn.microsoft.com/en-us/library/cc138362.aspx
11/8/2013 5:17 AM | T Joseph
Gravatar

# re: C# Fundamentals: Combining Enum Values with Bit-Flags

The [Flags] line of code is completely worthless.
7/1/2014 3:43 PM | MB
Gravatar

# re: C# Fundamentals: Combining Enum Values with Bit-Flags

[Flags] isnt worthless. Use
PropertyInfo.PropertyType.GetCustomAttributes(typeof(FlagsAttribute), false);
to generate user interface
8/23/2015 6:21 AM | Crime Wave 626
Gravatar

# re: C# Fundamentals: Combining Enum Values with Bit-Flags

Great article!
12/19/2016 8:13 AM | Pandian
Gravatar

# re: C# Fundamentals: Combining Enum Values with Bit-Flags

if ((value & MessagingProperties.Buffered) != 0)
should be
if ((value & MessagingProperties.Buffered) != MessagingProperties.Buffered)

While this may not matter in this case, it certainly does in the general case, where the mask could be multiple bits:
FileAttributes fileAttributes = File.GetAttributes(@"C:\foo");
FileAttributes mask = FileAttributes.Directory | FileAttributes.ReadOnly;
var isRoDir = (fileAttributes & mask) == mask;

Testing for != 0 will evaluate to true when file/folder is RO or a folder.
4/18/2017 11:35 AM | Andrew Dennison
Post A Comment
Title:
Name:
Email:
Comment:
Verification:
 

Powered by: