Why do I need the [Flags] Attribute?

As a developer you often take things for granted because they are “accepted best practices” until somebody asks about the most basic and simplest things, “why do I need [X]”, and you think, well that’s obvious, until you realize the (correct) answer was not so obvious at all, especially if the documentation is not accurate.

It happened to me when I came across the SO question, “What does the [Flags] Attribute Really do?

Time to investigate.

[Flags] can be attributes to Enums, and Enums are type-safe int’s (basically).

In C#, you can apply bitwise operators such as | and & (or, and), etc., so the documentation of the FlagsAttribute class

Indicates that an enumeration can be treated as a bit field; that is, a set of flags.

raises some questions: “can be?”, “as opposed to what?”, “is it really necessary?”

First, the enum values have to be powers of 2 to make the Flags attribute behave as expected.

Let’s take colors as example: 3 different color enumerations (yes, we only have 1-bit color depth here 😉 )

enum ColorEnum
{
	None,
	Red,
	Green,
	Blue
}

enum SimpleColor
{
	Red = 1,
	Green = 2,
	Blue = 4
}

[Flags]
enum FlagsColor
{
	Red = 1,
	Green = 2,
	Blue = 4,
	White = 7
}

ColorEnum can only take a single color, SimpleColor (powers of 2) *could* take a combination of colors, but lacks the [Flags] attribute, and FlagsColor has [Flags] and defines White as combination of Red|Green|Blue.

To test the different enum definitions, we call the enum’s .ToString() method, cast the enum values to int, and Parse() the result of the ToString() (using C#4 in VS2010):

ColorEnum ce = ColorEnum.Red;
Console.WriteLine("ColorEnum Red: " + ce.ToString());
Console.WriteLine("ColorEnum Red: " + ((int)ce).ToString());
Console.WriteLine("  parse: " + 
    Enum.Parse(typeof(ColorEnum), ce.ToString(), true).ToString());

which has the unsurprising result:

ColorEnum Red: Red
ColorEnum Red: 1
  parse: Red

A bit unexpected, but you can use bitwise operators on enums without a [Flag] attribute

ce = ColorEnum.Red | ColorEnum.Green;
Console.WriteLine("ColorEnum Red | Green: " + ce.ToString());
Console.WriteLine("  parse: " + 
    Enum.Parse(typeof(ColorEnum), ce.ToString(), true).ToString());

Since Red==1, Green==2, and Blue==3, the result is

ColorEnum Red | Green: Blue
  parse: Blue

Let’s experiment with SimpleColor, enum holding values of power of 2, but without the [Flags] attribute:

SimpleColor sc = SimpleColor.Red;
Console.WriteLine("SimpleColor Red: " + sc.ToString());
Console.WriteLine("  parse: " + 
    Enum.Parse(typeof(SimpleColor), sc.ToString(), true).ToString());

sc = SimpleColor.Red | SimpleColor.Green;
Console.WriteLine("SimpleColor Red | Green: " + sc.ToString());
Console.WriteLine("  parse: " + 
    Enum.Parse(typeof(SimpleColor), sc.ToString(), true).ToString());

Note that Red|Green gives 3, and the enum does not define a symbol for value 3:

SimpleColor Red: Red
  parse: Red
SimpleColor Red | Green: 3
  parse: 3

Finally, the FlagsColor enum with [Flags] attribute:

FlagsColor fc = FlagsColor.Red;
Console.WriteLine("FlagsColor Red: " + fc.ToString());
Console.WriteLine("  parse: " + 
    Enum.Parse(typeof(FlagsColor), fc.ToString(), true).ToString());
fc = FlagsColor.Red | FlagsColor.Green;
Console.WriteLine("FlagsColor Red | Green: " + fc.ToString());
Console.WriteLine("  parse: " + 
    Enum.Parse(typeof(FlagsColor), fc.ToString(), true).ToString());

Console.WriteLine("  parse FlagsColor as SimpleColor: " + 
    Enum.Parse(typeof(SimpleColor), fc.ToString(), true).ToString());
Console.WriteLine("  parse FlagsColor as ColorEnum: " + 
    Enum.Parse(typeof(ColorEnum), fc.ToString(), true).ToString());

The first part produces what is expected:

FlagsColor Red: Red
  parse: Red
FlagsColor Red | Green: Red, Green
  parse: Red, Green

But what about the stringified bit combination “Red, Green” if it is parsed by the other enums?

  parse FlagsColor as SimpleColor: 3
  parse FlagsColor as ColorEnum: Blue

Seems that the Enum.Parse() method ignores the (lack of the) [Flags] attribute!

The .HasFlag() method also works regardless of the [Flags] attribute:

Console.WriteLine("ColorEnum has Red? " + ce.HasFlag(ColorEnum.Red));
Console.WriteLine("SimpleColor has Red? " + sc.HasFlag(SimpleColor.Red));
Console.WriteLine("FlagsColor has Red? " + fc.HasFlag(FlagsColor.Red));

Console.WriteLine("ColorEnum has Blue? " + ce.HasFlag(ColorEnum.Blue));
Console.WriteLine("SimpleColor has Blue? " + sc.HasFlag(SimpleColor.Blue));
Console.WriteLine("FlagsColor has Blue? " + fc.HasFlag(FlagsColor.Blue));

results in:

ColorEnum has Red? True
SimpleColor has Red? True
FlagsColor has Red? True
ColorEnum has Blue? True
SimpleColor has Blue? False
FlagsColor has Blue? False

Finally, testing a composite bit value:

fc = FlagsColor.Red | FlagsColor.Green | FlagsColor.Blue;
Console.WriteLine("FlagsColor RGB: " + fc.ToString());
Console.WriteLine("  parse: " + 
    Enum.Parse(typeof(FlagsColor), "Red, Green, Blue", true).ToString());
Console.WriteLine("FlagsColor has White? " + fc.HasFlag(FlagsColor.White));

results in

FlagsColor RGB: White
  parse: White
FlagsColor has White? True

If you work with VB.Net rather than C#, this entry on social.msdn may be interesting for you:

Although C# happily allows users to perform bit operations on enums without the FlagsAttribute, Visual Basic does not. So if you are exposing types to other languages, then marking enums with the FlagsAttribute is a good idea.

It also states

(The [Flags] attribute) makes it clear that the members of the enum are designed to be used together.

Some things should be described more explicitly in the documentation.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: