Hoist those flags!
In the course of some work on WSS utils (a log class) quite a while ago, a fellow developer asked me about the Flags attribute I was using. Now, rightly or wrongly, I tend to festoon my code with these so it looks more like bunting, and I just love them. I think I may be a repressed used car salesman. So in honour of the recently passed Australia Day, and our recovery from it, I'd like to promote some jingoistic Green and Gold FlagsAttribute waving. 
The easiest way to think of flags, (if you don't know dip switches, chmod and the like), is a pattern of bits in the base 2 orders of magnitude, where 1 is on and 0 is off:
1 2 4 8 16 32
0 1 0 1 0 0
off on off on off off
Hang out the bunting:
So to promote the use of these little beauties, I'll give a quick low-down here. I'll cover more of the wonders of attributes and reflection in another post, when I blog on my my web config updater in a few days.
How do you string them over your code? Easy:
[
Flags]
public enum DestinationTypes : int { EventLog=1, File=2, Email=4, SharePoint=8, SysLog=16, Log4Net=32};
That's looking more festive already, fluttering at the top of the class. Why the base 2 enum values? Well, that gives us that wonderful ability to turn on, or off, any number of these brightly coloured triangles without getting them all twisted up. What's more, you could even add a composite type into the definition like this:
public enum DestinationTypes : int { EventLog = 1, File = 2, Email = 4, SharePoint = 8, SysLog = 16, Log4Net = 32, NonMS = SysLog | Log4Net };
Just be careful with those composite flags, as it's easy to get tangled in the implications of turning on what looked like a single flag!
Seeing as I'm ambidextrous, here's how to turn two flags on:
public
static DestinationTypes NotYetImplemented = DestinationTypes.SysLog | DestinationTypes.Log4Net;..and it just as easy as the above 'bitwise or' if you have an existing value and you want to flick a single switch in there - just use the old = assignment shortcut notation with it:
DestinationTypes addDestinationsToMe = DestinationTypes.SysLog | DestinationTypes.Log4Net;
addDestinationsToMe |= DestinationTypes.SharePoint;
Lets save energy, and turn a flag off now. The 'bitwise and' is the go:
DestinationTypes takeDestinationFromMe = DestinationTypes.SysLog | DestinationTypes.Log4Net;
takeDestinationFromMe &= DestinationTypes.SharePoint;
But how do I test if the sharepoint flag is on, or off, if I can't see the semaphore?
The bitwise operator boys come to our rescue again, in the form of the 'bitwise and'. Test for a value like this:
if ((addDestinationsToMe & DestinationTypes.SharePoint) == DestinationTypes.SharePoint)
{}
Now there's only one thing left, and that's iterating through ALL types. You don't need to dig into reflection to do this - just use following simple snippet:
foreach
(DestinationTypes dest in Enum.GetValues(typeof(DestinationTypes)))
So what does this darn FlagsAttribute do for me, as my enum would work fine without it, and it still sequentially numbers the elements, even with the flags attribute there, unless I set my own values?
Thats a good question. Apart from annotating the type for reflection, there is only one small difference...
class Program
{
public enum DestinationTypesNoAttribute : int { EventLog = 1, File = 2, Email = 4, SharePoint = 8, SysLog = 16, Log4Net = 32 };
[Flags]
public enum DestinationTypes : int { EventLog = 1, File = 2, Email = 4, SharePoint = 8, SysLog = 16, Log4Net = 32 };
static void Main(string[] args)
{
string name = typeof(DestinationTypes).FullName;
// name = "ConsoleApplication1.Program+DestinationTypes"
name = DestinationTypes.SharePoint.GetType().Name;
// name = "DestinationTypes"
name = DestinationTypes.SharePoint.GetType().FullName;
// name = "ConsoleApplication1.Program+DestinationTypes"
string strVal = DestinationTypes.SharePoint.ToString();
// strVal = "SharePoint"
int intVal = (int)DestinationTypes.SharePoint;
// intVal = 8
intVal = (int)(DestinationTypes.Email | DestinationTypes.SharePoint);
// intVal = 12 (ie 001100 binary = 12 decimal)
strVal = (DestinationTypes.SharePoint | DestinationTypes.Email).ToString();
// *** strVal = "Email, SharePoint"
name = typeof(DestinationTypesNoAttribute).FullName;
// name = "ConsoleApplication1.Program+DestinationTypesNoAttribute"
name = DestinationTypesNoAttribute.SharePoint.GetType().Name;
// name = "DestinationTypesNoAttribute"
name = DestinationTypesNoAttribute.SharePoint.GetType().FullName;
// name = "ConsoleApplication1.Program+DestinationTypesNoAttribute"
strVal = DestinationTypesNoAttribute.SharePoint.ToString();
// strVal = "SharePoint"
intVal = (int)DestinationTypesNoAttribute.SharePoint;
// intVal = 8
intVal = (int)(DestinationTypesNoAttribute.Email | DestinationTypesNoAttribute.SharePoint);
// intVal = 12 (ie 001100 binary = 12 decimal)
strVal = (DestinationTypesNoAttribute.SharePoint | DestinationTypesNoAttribute.Email).ToString();
// *** strVal = "12"
}
}
So there you have it! When serialising, you get the CSV format, not the integer value - very nice if you need to decipher or edit a stream. Note the '+' annotation to show a field member of the class, but as you would expect, the value isn't considrered a type.
Finally, given that these are still enums, remember you can cast,test and use them as integers (although you might get compiler warnings you can ignore - just ensure you use valid values for your range).
Now there are no more excuses for your classes to be dull, just hang some flags up and flick the switch!