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.