Enums

An enum (enumeration) is a type with a fixed set of named values: days of the week, order statuses, log levels, directions. Using an enum instead of magic numbers or strings makes code readable, lets the compiler catch typos, and lets your editor suggest the valid options.

Declaring and using an enum

C#
OrderStatus status = OrderStatus.Pending;
Console.WriteLine(status);

status = OrderStatus.Shipped;
if (status == OrderStatus.Shipped)
{
    Console.WriteLine("On its way!");
}

enum OrderStatus
{
    Pending,
    Paid,
    Shipped,
    Delivered,
    Cancelled,
}
Output
Pending
On its way!

Compare with storing the status as a string: status == "shiped" compiles happily and silently never matches. OrderStatus.Shiped is a compile error.

Underlying values

Each enum member is backed by an integer. By default the first is 0 and each next one is 1 higher, but you can choose the values. Casting converts between an enum and its number:

C#
Console.WriteLine((int)Priority.Low);
Console.WriteLine((int)Priority.Critical);
Console.WriteLine((Priority)20);
Console.WriteLine((Priority)7);          // no such member: just the number
Console.WriteLine(Enum.IsDefined((Priority)7));
Console.WriteLine(Priority.High > Priority.Medium);

enum Priority
{
    Low = 1,
    Medium = 10,
    High = 20,
    Critical = 100,
}
Output
1
100
High
7
False
True

Note that casting an arbitrary number to an enum always succeeds, even if no member has that value — check with Enum.IsDefined when the number comes from outside your program. Always give explicit values if they're stored in a database or file, so reordering the members later doesn't change the meaning of existing data.

Enums with switch

Enums and switch expressions are a natural pair. The compiler even warns if you don't handle every member:

C#
foreach (TrafficLight light in Enum.GetValues<TrafficLight>())
{
    Console.WriteLine($"{light,-6} -> {Action(light)}");
}

static string Action(TrafficLight light) => light switch
{
    TrafficLight.Red => "stop",
    TrafficLight.Amber => "get ready",
    TrafficLight.Green => "go",
    _ => throw new ArgumentOutOfRangeException(nameof(light)),
};

enum TrafficLight { Red, Amber, Green }
Output
Red    -> stop
Amber  -> get ready
Green  -> go

Enum.GetValues<T>() returns every member of an enum. The _ arm handles numbers that aren't valid members.

Converting from text

Enum.Parse and Enum.TryParse turn a name into a member — handy for configuration files and user input. Pass ignoreCase: true to accept any capitalisation:

C#
foreach (string input in new[] { "Saturday", "monday", "Funday" })
{
    if (Enum.TryParse(input, ignoreCase: true, out DayOfWeek day))
        Console.WriteLine($"{input} -> {day} ({(int)day})");
    else
        Console.WriteLine($"{input} -> not a day");
}

Console.WriteLine(string.Join(", ", Enum.GetNames<DayOfWeek>()));
Output
Saturday -> Saturday (6)
monday -> Monday (1)
Funday -> not a day
Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday

DayOfWeek is one of many enums built into .NET; others include ConsoleColor, StringComparison and FileMode.

Flags: combining values

Sometimes several options can apply at once — read and write permission, bold and italic text. Give each member its own bit (1, 2, 4, 8 …) and add the [Flags] attribute. Combine with | and test with HasFlag or &:

C#
Permissions user = Permissions.Read | Permissions.Write;
Console.WriteLine(user);
Console.WriteLine(user.HasFlag(Permissions.Write));
Console.WriteLine(user.HasFlag(Permissions.Delete));

user |= Permissions.Delete;     // add a flag
user &= ~Permissions.Write;     // remove a flag
Console.WriteLine(user);

Console.WriteLine(Permissions.All);
Console.WriteLine((int)Permissions.All);

[Flags]
enum Permissions
{
    None = 0,
    Read = 1,
    Write = 2,
    Delete = 4,
    Share = 8,
    All = Read | Write | Delete | Share,
}
Output
Read, Write
True
False
Read, Delete
All
15

The [Flags] attribute is what makes ToString print Read, Write instead of the number 3. Writing the values in binary (0b0001, 0b0010 …) or as shifts (1 << 2) makes the bit pattern clear.

Enums are value types

An enum variable is just an integer underneath, so it's cheap to store and compare. Its default value is whatever member equals 0 — which is why it's good practice to make 0 a sensible default such as None or Unknown:

C#
var task = new TaskItem();
Console.WriteLine(task.Status);

class TaskItem
{
    public TaskStatus Status { get; set; }
}

enum TaskStatus { NotStarted, InProgress, Done }
Output
NotStarted

Try it yourself

Use an enum for the size of a coffee and a switch expression for its price. Then parse an order from text.

Try it yourself · Program.cs
Edit the code, copy it into Program.cs and run it with dotnet run Program.cs (.NET 10 or later). How to set up .NET
Output
Small: $2.40
Large: $3.40
Medium: $2.90
Unknown size 'huge'