Smarter conditions with flags and enums
Tags: csharp, dotnet, programming, tutorial
Published: 2022-06-27 05:40:05

Enumeration are commonly used to define a set of mutually exclusive values (ex: colors, days of the week, months, etc.).

When working with them, we often want to check that a given value is part of a subset of our enumeration. Unfortunately, it may result in verbose conditions or in several of them being quite long and prone to bugs due to their length.

Hopefully, .NET has a pretty neat feature that can help you having smarter and cleaner conditions in such cases: let's explore it!

## Use case

For our use case, let's create a `DaysOfTheWeek` enum with all of them:

```csharp
public enum DaysOfTheWeek
{
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    Sunday,
}
```

We then want to compute the price of a given service depending of the day of the week. 

Our algorithm is not very hard but has a couple of conditions:

- If the provided day is in the week-end, it is `3`
- If the provided day is Monday, Tuesday or Friday, it is `1` because those are counted as special days by the company
- Otherwise, the price will just be `2`

A first implementation of those rules might be:

```csharp
public int GetRate(DaysOfTheWeek day)
{
    if (day == DaysOfTheWeek.Saturday
        || day == DaysOfTheWeek.Sunday)
    {
        return 3;
    }

    if (day == DaysOfTheWeek.Monday
        || day == DaysOfTheWeek.Tuesday
        || day == DaysOfTheWeek.Friday)
    {
        return 1;
    }

    return 2;
}
```

There are, however, some disadvantages to such algorithm:

- The method looks pretty "complicated" for three simple conditions (many ORs and a lot of chars)
- The logic we implemented is not very explicit and having someone looking at the code will certainly result in questions asking how are those days chosen

## Introducing the `Flags` attribute

With the `[Flags]` attribute, we can use our enum values as flags an perform bitwise comparison on them without too much pain

To do so, we only have to slightly modify our enumeration by:

1. Adding the `[Flags]` attribute to it
1. Assigning each enumeration constant a power of two so that they will not overlap each other when combining them

Doing so will result in the following code:

```csharp
[Flags]
public enum DaysOfTheWeek
{
    Monday    = 1,
    Tuesday   = 2,
    Wednesday = 4,
    Thursday  = 8,
    Friday    = 16,
    Saturday  = 32,
    Sunday    = 64,
}
```

By just updating our enumeration, we didn't altered our code and the previous function still works great. However, let's now use our flags for the conditions using the `HasFlag` method:

```csharp
public int GetRate(DaysOfTheWeek day)
{
    if ((DaysOfTheWeek.Saturday | DaysOfTheWeek.Sunday).HasFlag(day))
    {
        return 3;
    }

    if ((DaysOfTheWeek.Monday | DaysOfTheWeek.Tuesday | DaysOfTheWeek.Friday).HasFlag(day))
    {
        return 1;
    }

    return 2;
}
```

This is still very verbose and does not solve anything right away but one interesting thing about flags is that we can define values in our enumeration that are a combination of others.

In our case, by having a dedicated value for the weekend and another one for the special days, we might succeed in improving the overall readability a little bit (pun intended), let's do that:

```diff
[Flags]
public enum DaysOfTheWeek
{
    Monday    = 1,
    Tuesday   = 2,
    Wednesday = 4,
    Thursday  = 8,
    Friday    = 16,
    Saturday  = 32,
    Sunday    = 64,
+   Weekend    = Saturday | Sunday,
+   SpecialDay = Monday | Tuesday | Friday,
}
```

By introducing those values, our code is suddenly much straightforward but also self-explanatory as for why there is such conditions:

```csharp
public int GetRate(DaysOfTheWeek day)
{
    if (DaysOfTheWeek.Weekend.HasFlag(day))
    {
        return 3;
    }

    if (DaysOfTheWeek.SpecialDay.HasFlag(day))
    {
        return 1;
    }

    return 2;
}
```

## Take aways

Using flags, we were able to combine several enumeration constants onto a single one to clarify the intent of our algorithm by only slightly altering it

However, flags might not always be the solution for those problems: if you have only a small subset of constants that you want to check a value against, it might not be worth the complexity

Similarly, introducing flags to an enumeration that is persisted in a database will require you to update all your previous stored records

Finally, you should be careful about the powers of two that you are using: a wrong value or an overlapping one might introduce nasty bugs

> As a side note, be very careful when using flags for access rights not to grant anything for the value `0` as `0.HasFlag(/*anything*/)` will return `true`

## Additional tip

As a side note, it may be too verbose to write the powers of two yourself. You can use the shift-left operator to let the computer do that for you!

```csharp
[Flags]
public enum DaysOfTheWeek
{
    Monday     = 1<<0,  // 1
    Tuesday    = 1<<1,  // 2
    Wednesday  = 1<<2,  // 4
    Thursday   = 1<<3,  // 8
    Friday     = 1<<4,  // 16
    Saturday   = 1<<5,  // 32
    Sunday     = 1<<6,  // 64
    Weekend    = Saturday | Sunday,
    SpecialDay = Monday | Tuesday | Friday,
}
```

I hope that you learned something useful, happy coding!