Enums | Bit Flags

08/15/2013 09:53 »Barney«#1
So first of all, this is my first real tutorial here so just go easy on me, Ok ?

If you have some experience with programming languages in general, you must have heard about Enumerations (enums, for short) (And I'm not talking about dynamic collections, neither arrays. Enumerations are known at compile time, which means that they have constant values)

One advantage of using them is that you can't find yourself later on trying to fix a KeyNotFoundException or an IndexOutOfRangeException, since the compiler will give you an error (cleverly enough, I might say).

Also, you should know that there are various ways to create enumerations:
  • static classes with constant members
  • some languages offer the enum data type
(okay, there might be more ways but you got the point)


So, (finally), what are Flags when talking about enums ? Well, while we regurarly use enums to express a single, constant value, there are cases when we need to have more constants in a single object.

Since this is a C# tutorial, the attribute I'm going to use is the System.FlagsAttribute. This sexy guy can be used on enums, and even though it doesn't do anything special, it tells the programmer "HEY, THIS ENUMERATION'S VALUES CAN BE MIXED UP".

Code:
[Flags]
public enum DaysOfWeek { 
	None = 0,
	Monday = 1,
	Tuesday = 2,
	Wednesday = 4,
	Thursday = 8,
	Friday = 16,
	Saturday = 32,
	Sunday = 64
}
This code snippet doesn't do much, but you might wonder why the hell is there a None member ? Well, it's good practice to have a None member in any Flags-marked enumeration.
Code:
if (this.WorkDays == DaysOfWeek.None) { }
is much better than:
Code:
if (this.WorkDays == 0) { }
So, let's see how we can use these things. I will explain everything in more detail after the code section.

Code:
class MyExampleProgram {
	[Flags]
	public enum DaysOfWeek { 
		None = 0,
		Monday = 1,
		Tuesday = 2,
		Wednesday = 4,
		Thursday = 8,
		Friday = 16,
		Saturday = 32,
		Sunday = 64
	}
	
	static void Main() {
		DaysOfWeek workDays = default (DaysOfWeek); // will be set to DaysOfWeek.None (= 0)
		Console.Write("Please write the day you would like to go to work this week (please be serious): ");
		int input = int.Parse(Console.ReadLine());
		
		workDays = (DaysOfWeek) input; // notice the explicit cast
		Console.Write("You will go to work on {0}. Would you like to go to work on an other day too ? Press ENTER if your answer is no.");
		if (Console.ReadLine() == "")
			return;
			
		Console.Write("Please write the other day you would like to go to work this week (please be serious): ");
		int secondInput = int.Parse(Console.ReadLine());
		
		workDays = workDays | (DaysOfWeek) secondInput; 
		// notice we don't use the + operator,
		// insted we will use the Bitwise OR operator
		
		// the same line of code could have been written this way:
		// workDays |= (DaysOfWeek) secondInput;
	}
}
The OR operator combines the two values. If we had these two 4-bit integers in binary: 0110 (= 5) and 1001 (= 9), 0110 | 1001 = 1111 (= 15)

What if we want to check if an enum object has a specific flag? BAM, the Bitwise AND operator (&):

Code:
if (workDays.HasFlag(DaysOfWeek.Monday))
{
	// you're not very lucky
}
this is equivalent to:
Code:
if (workDays & DaysOfWeek.Monday) {
	// you're not very lucky 
}
Implementation of the HasFlag method:
Code:
public class Enum {
	....
	
	public bool HasFlag(Enum flag) {
		return this & flag != 0;
	}
	
	....
}
What if we get sick, or just want to stay in bed for a day? How do we remove a day of work? (programatically speaking, lol)
Here is where we meet the bitwise Negation (~) operator:

Code:
workDays = workDays & ~DaysOfWeek.Saturday; // ...
or even
Code:
workDays &= ~DaysOfWeek.Saturday; // a bit shorter


So, let's review it:
  • add a flag: the OR operator
  • check for a flag: the AND operator
  • remove a flag: the AND; Negation operator

If you have any other questions just post them here ;)

PS: Yes, the values of the DaysOfWeek enum aren't correct here, since Monday | Tuesday = Wednesday, but I think you got the idea. When designing your enums' flags, make sure this won't happen if it isn't a logical combination.
08/15/2013 17:09 snow#2
Nice tutorial, thanks for that. :)

1 thing I noticed:

-Does XORing the enum really work?

DaysOfWeek workDays = default(DaysOfWeek);
workDays ^= DaysOfWeek.Monday;
that should be 0 ^= 1 -> 1, right?

A better approach should be workDays &= ~DaysOfWeek.Monday, that's a logical AND with the negation of Monday -> 0 &= ~1.
At least that's how I solve logical combinations. :)
Would be bad if you'd have to work for an extra day, lol
08/16/2013 09:38 »Barney«#3
Quote:
Originally Posted by snow911 View Post
Nice tutorial, thanks for that. :)

1 thing I noticed:

-Does XORing the enum really work?

DaysOfWeek workDays = default(DaysOfWeek);
workDays ^= DaysOfWeek.Monday;
that should be 0 ^= 1 -> 1, right?

A better approach should be workDays &= ~DaysOfWeek.Monday, that's a logical AND with the negation of Monday -> 0 &= ~1.
At least that's how I solve logical combinations. :)
Would be bad if you'd have to work for an extra day, lol
You're right, it should be:

Code:
if (workDays & DaysOfWeek.Monday != DaysOfWeek.None)
      workDays ^= DaysOfWeek.Monday;
08/16/2013 19:52 Schlüsselbein#4
Or simply: workDays &= ~DaysOfWeek.Monday

xor is used to toggle certain bits.
02/23/2015 09:41 tomicrow#5
More about....Enum

[Only registered and activated users can see links. Click Here To Register...]

Tomi