Enums and the HasFlag method (or 'How "1 + 0 = 1" can Ruin Your Day')

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 Flagsattribute 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.