.NET Academy

Enums

Replace magic numbers and strings with named constants the compiler can check.

9 min read

You will learn

  • Declare an enum and use its values
  • Switch over enum values
  • Convert between enums, strings and numbers
  • Combine options with [Flags] enums

Magic values are fragile

How would you store an order's status? A string like "shipped" invites typos ("shiped") that nobody catches until production. A number like 2 means nothing to the next reader. An enum gives a fixed set of named values that the compiler checks:

enum OrderStatus
{
    Pending,
    Paid,
    Shipped,
    Delivered,
    Cancelled,
}
 
var status = OrderStatus.Paid;
 
if (status == OrderStatus.Shipped) { ... }
status = OrderStatus.Shiped;   // ❌ compile error — typo caught immediately

Enum types and their members use PascalCase. Enums are value types.

Enums are numbers underneath

Each member has an integer value, starting at 0 by default. You can set them explicitly:

enum HttpStatus
{
    Ok = 200,
    NotFound = 404,
    ServerError = 500,
}
 
int code = (int)HttpStatus.NotFound;     // 404
var status = (HttpStatus)200;            // HttpStatus.Ok

Watch out

Casting any integer to an enum compiles — (OrderStatus)99 is allowed, even though there's no such member. Validate numbers from outside with Enum.IsDefined.

Enums and switch

Enums pair naturally with switch expressions:

string Describe(OrderStatus s) => s switch
{
    OrderStatus.Pending => "Waiting for payment",
    OrderStatus.Paid => "Preparing your order",
    OrderStatus.Shipped => "On its way",
    OrderStatus.Delivered => "Delivered",
    OrderStatus.Cancelled => "Cancelled",
    _ => throw new ArgumentOutOfRangeException(nameof(s)),
};

Converting to and from strings

Console.WriteLine(OrderStatus.Shipped);              // "Shipped" — ToString gives the name
 
if (Enum.TryParse<OrderStatus>("delivered", ignoreCase: true, out var parsed))
    Console.WriteLine(parsed);                       // Delivered
 
foreach (var s in Enum.GetValues<OrderStatus>())     // every member
    Console.WriteLine($"{(int)s}: {s}");

[Flags]: combining options

Some settings aren't "one of", but "any combination of": file permissions, days of the week a shop is open, text styles. A flags enum gives each option its own bit:

[Flags]
enum Days
{
    None = 0,
    Monday = 1,
    Tuesday = 2,
    Wednesday = 4,
    Thursday = 8,
    Friday = 16,
    Saturday = 32,
    Sunday = 64,
    Weekend = Saturday | Sunday,
}
 
var open = Days.Monday | Days.Wednesday | Days.Saturday;   // combine with |
 
Console.WriteLine(open);                          // Monday, Wednesday, Saturday
Console.WriteLine(open.HasFlag(Days.Wednesday));  // True
Console.WriteLine((open & Days.Weekend) != 0);    // True — open on at least one weekend day
 
open &= ~Days.Monday;                             // remove a flag

Try it

Program.cs
Loading editor…

C# runtime not loaded

Key takeaways

  • Enums are named sets of constants — checked by the compiler, readable in code.
  • Each member has an integer value (from 0 by default); cast to convert.
  • ToString() and Enum.TryParse convert to and from names.
  • [Flags] enums with power-of-two values can be combined with | and checked with HasFlag.

Exercise

Implement Next (Red → Green → Yellow → Red) and DurationSeconds (Red 30, Green 25, Yellow 5) for the traffic light. Then implement CanEdit, which is true only when the permissions include both Read and Write.

Program.cs
Loading editor…

C# runtime not loaded

Hint 1

Switch expressions are perfect here: light switch { TrafficLight.Red => TrafficLight.Green, ... }.

Hint 2

HasFlag checks for one or more flags: permissions.HasFlag(Permission.Read | Permission.Write).

Show solution
var light = TrafficLight.Red;
for (int i = 0; i < 4; i++)
{
    Console.WriteLine($"{light} for {DurationSeconds(light)}s");
    light = Next(light);
}

TrafficLight Next(TrafficLight light) => light switch
{
    TrafficLight.Red => TrafficLight.Green,
    TrafficLight.Green => TrafficLight.Yellow,
    _ => TrafficLight.Red,
};

int DurationSeconds(TrafficLight light) => light switch
{
    TrafficLight.Red => 30,
    TrafficLight.Green => 25,
    TrafficLight.Yellow => 5,
    _ => throw new ArgumentOutOfRangeException(nameof(light)),
};

bool CanEdit(Permission permissions) =>
    permissions.HasFlag(Permission.Read | Permission.Write);

enum TrafficLight { Red, Yellow, Green }

[Flags]
enum Permission
{
    None = 0,
    Read = 1,
    Write = 2,
    Delete = 4,
}

Quiz

4 questions · 3 correct to pass
  1. 1.Given `enum Size { Small, Medium, Large }`, what is `(int)Size.Large`?

  2. 2.What does `Size.Medium.ToString()` return?

  3. 3.Why do [Flags] enums use values 1, 2, 4, 8…?

  4. 4.What's the safest way to turn user text like "Large" into a Size?

To complete this lesson

  • ○ Solve the exercise
  • ○ Pass the quiz (3/4 correct)