Enums and the HasFlag method (or 'How "1 + 0 = 1" can Ruin Your Day')
In .NET an enum
is a type used to denote a complete set of simple constants. A simple example being the System.DayOfWeek
enumeration.
Enums can be decorated with the Flags
attribute which means that they can be combined. For instance, we may have an enumeration listing different pets that a family may have.
[Flags]
public enum Pet
{
None,
Dog,
Cat,
/* ... More pets here ... */
}
Because a family may have more than one pet we can use the Flags
attribute to denote this, allowing us to combine values.
Family family1 = new Family
{
Pets = Pet.None
};
Family family2 = new Family
{
Pets = Pet.Dog
};
Family family3 = new Family
{
Pets = Pet.Dog | Pet.Cat
};
Family[] allFamilies = new [] { family1, family2 family3 };
We can then get all the families who have a dog by doing something like the following
var familiesWithDogs = allFamilies.Where(family => family.Pets.HasFlag(Pet.Dog));
This all works well once you have an option like None at the start. In C# an enum will by default have a backing int value and the first value in the enum will have a default value of zero
The HasFlag
method returns true if the enum we're checking for has a value of zero. This happens because the HasFlag method returns the value of valueToCheck & flag = flag
.
So if we have an enum that represents a series of options where there is no valid None
option we can avoid this by simply seeding the first enum with a value of 1
[Flags]
public enum OptionSet
{
Option1 = 1,
Option2
Option3
}
This forces a non-zero value so testing for the following will behave as expected
var options = OptionSet.Option2 | OptionSet.Option3;
var containsOption1 = options.HasFlag(OptionSet.Option1); // false
This is a prime example of why it can be a good idea to explicitly set the value of an enum, particularly the first entry. If there is a valid None
option it should be given the explicit value of zero to avoid unexpected behaviour if a new enum value is inserted before it.