DEV Community

Cover image for Smarter conditions with flags and enums
Pierre Bouillon for SFEIR

Posted on

Smarter conditions with flags and enums

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:

public enum DaysOfTheWeek
{
    Monday,
    Tuesday,
    Wednesday,
    Thursday,
    Friday,
    Saturday,
    Sunday,
}
Enter fullscreen mode Exit fullscreen mode

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:

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;
}
Enter fullscreen mode Exit fullscreen mode

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
  2. 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:

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

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:

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;
}
Enter fullscreen mode Exit fullscreen mode

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:

[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,
}
Enter fullscreen mode Exit fullscreen mode

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

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

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

    return 2;
}
Enter fullscreen mode Exit fullscreen mode

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!

[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,
}
Enter fullscreen mode Exit fullscreen mode

I hope that you learned something useful, happy coding!

Discussion (0)