Understanding Flag Enums in C#

Efficiently representing combination of choices

Enumeration types, also known as enum types, are widely used by C# developers to improve code readability and maintainability, by offering a standardized way to represent a set of related numeric constants. Good examples are days of the week, seasons, or a predefined range of colors.

In this article I’m going to talk about flag enums, which are a special case, that can be used to represent a combination of binary choices into a single value using bitwise operations.


Imagine you were creating a simple alarm application were you could set alarms for a given time and weekdays. If you were using a simple enum, the Alarm class would need a collection to store the selected days, ideally an hash set to prevent duplicates and faster lookups. It would look similar to this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public enum Weekday
{
None = 0,
Sunday,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
}

public class Alarm
{
public ICollection<Weekday> Weekdays { get; } = new HashSet<Weekday>();

public TimeOnly Time { get; set; }
}

An an example, if you needed to create an alarm to set off at 7 a.m. only during workdays and needed to execute some conditional logic depending your selection, the code could be as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
var alarm = new Alarm
{
Time = new TimeOnly(07, 00, 00),
Weekdays =
{
Weekday.Monday,
Weekday.Tuesday,
Weekday.Wednesday,
Weekday.Thursday,
Weekday.Friday,
Weekday.Saturday,
}
};

// disable a flag
alarm.Weekdays.Remove(Weekday.Saturday);

// check if a flag is enabled
if (alarm.Weekdays.Contains(Weekday.Monday))
{

}

// check if some flags are enabled
if (alarm.Weekdays.Any(e => e is Weekday.Saturday or Weekday.Sunday))
{

}

// check if a flag is disabled
if (!alarm.Weekdays.Contains(Weekday.Friday))
{

}

The code is relatively simple and easy to understand but it has two drawbacks — memory allocations of new collections and constant iterations to check for flags. How can we solve this?

Introducing flag enums

Flag enums were created to optimize both memory and CPU usage by providing a way to store multiple options into a single variable.

The core concept is the following: every variable is stored in memory as binary data and CPUs are very efficient at making bitwise operations so, if you ensure each enum value is represented by a single bit that doesn’t overlap another value, you can use binary operators (AND, OR, NOT and XOR) to store multiple options into a single primitive variable.

By default, enums are represented in memory as an integer, but can also be a short or a long value. If each bit represents a different enum value, this means you can have up to 64 possible values that must not overlap each other.

Lets change the previous example to use a flag enum instead:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[Flags]
public enum Weekday
{
None = 0,
Sunday = 1 << 0, // 0b_00000001 // 1
Monday = 1 << 1, // 0b_00000010 // 2
Tuesday = 1 << 2, // 0b_00000100 // 4
Wednesday = 1 << 3, // 0b_00001000 // 8
Thursday = 1 << 4, // 0b_00010000 // 16
Friday = 1 << 5, // 0b_00100000 // 32
Saturday = 1 << 6, // 0b_01000000 // 64
}

public class Alarm
{
public Weekday Weekdays { get; set; }

public TimeOnly Time { get; set; }
}

As you can see, some small changes were made:

  1. Added the Flags attribute to the enum — not a requirement, but I’ll explain later why it should be used;
  2. Defined the value of each option ensuring no bit was overlapped — in this case I used the shift left operator to make it easier and more readable;
  3. Removed the collection and changed the property Weekdays to a simple Weekday enum;

You may now be asking: you removed the collection and changed it to a single Weekday value, how am I supposed to represent multiple options then?

Introducing bitwise operators

As I stated before, bitwise operators (AND, OR, XOR, NOT) allow us to execute logical computation between the bits of two integral operands.

This is relevant because if we use a bitwise OR (|) operator we can store the result as an aggregation of multiple active flags. Imagine you wanted a variable representing the weekend (setting Saturday and Sunday flags):

1
2
3
var weekend = Weekday.Saturday | Weekday.Sunday;
// = 0b_01000000 | 0b_00000001
// = 0b_01000001

As you can see in the comment showing the binary representation, both flags will be set.

On the other hand, if you want to check if a given flag is active you can simply use the bitwise AND (&) operator and compare the result to zero (or Weekday.None).

Lets check if Sunday is part of the weekend by checking a non zero result:

1
2
3
4
var hasSunday = (weekend & Weekday.Sunday) != Weekday.None;
// = (0b_01000001 & 0b_00000001) != 0
// = 0b_00000001 != 0
// = true

If you want to check if a flag is disabled the operation result must be zero:

1
2
3
4
var noMonday = (weekend & Weekday.Monday) == Weekday.None;
// = (0b_01000001 & 0b_00000010) == 0
// = 0b_00000000 == 0
// = true

And finally, if you want to disable a given flag just do a bitwise AND against a negated flag value:

1
2
3
4
var sunday = weekend & ~Weekday.Saturday;
// = 0b_01000001 & ~0b_01000000
// = 0b_01000001 & 0b_10111111
// = 0b_00000001

Lets update the original example and use the bitwise operators we just learned (AND, OR, NOT):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var alarm = new Alarm
{
Time = new TimeOnly(07, 00, 00),
Weekdays = Weekday.Monday |
Weekday.Tuesday |
Weekday.Wednesday |
Weekday.Thursday |
Weekday.Friday |
Weekday.Saturday,
// Weekdays = (Weekday) 0b_01111110
};

// disable a flag
alarm.Weekdays &= ~Weekday.Saturday; // (Weekday) 0b_00111110

// check if a flag is enabled
if ((alarm.Weekdays & Weekday.Monday) != 0)
{

}

// check if some flags are enabled
if ((alarm.Weekdays & (Weekday.Saturday | Weekday.Sunday)) != Weekday.None)
{

}

// check if a flag is disabled
if ((alarm.Weekdays & Weekday.Friday) == Weekday.None)
{

}

Right now you must be thinking: sure, this code is more resource efficient and performant but it certainly is harder to read… not sure if I gained something… how can I improve this?

Well, because checking if a flag is active is a very common scenario, all enums have a method called HasFlag that can be used just for that. Lets rewrite the example in a much simpler and readable way:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var alarm = new Alarm
{
Time = new TimeOnly(07, 00, 00),
Weekdays = Weekday.Monday |
Weekday.Tuesday |
Weekday.Wednesday |
Weekday.Thursday |
Weekday.Friday |
Weekday.Saturday,
};

alarm.Weekdays &= ~Weekday.Saturday;

if (alarm.Weekdays.HasFlag(Weekday.Monday))
{

}

if (alarm.Weekdays.HasFlag(Weekday.Saturday) || alarm.Weekdays.HasFlag(Weekday.Sunday))
{

}

if (!alarm.Weekdays.HasFlag(Weekday.Friday))
{

}

It would be nice to also have a method to disable a flag but none is provided by the framework, so you’ll have to stick with bitwise operators for that scenario.

Why the [Flags] attribute?

Earlier I said marking the enum with a Flags attribute is opcional (everything we talked about will still work) but still recommended because it helps the IDE to give you more detailed warnings or suggestions, but it’s specially important for debugging, parsing or converting to a string representation.

Imagine the weekend scenario were both Saturday and Sunday flags are set. If the enum doesn’t have the Flags attribute and you go inspect the variable, the IDE will show the value 65 (0b_01000001) because it isn’t defined in the enum. Same behavior for the ToString method, that will return "65":

If on the contrary the Flags attribute is used, both IDE and ToString method will show a proper representation of what flags are active:

Conclusion

I hope this article gave you a good idea on how to use flag enums and bitwise operations to store and check for active flags using a single field, making your application more resource efficient without losing code readability.