Site

Enums — C# Enumeration Types

Tutorial 8.0  •  C# / Learn

8.0 What This Teaches

This tutorial covers C# enumeration types: Note: C# enums are named integer constants. Rust enums carry data per variant and are more powerful. Python enums (from the enum module) are objects with methods.

8.1 Basic Enum Syntax

enum Direction { North, South, East, West }

Direction d = Direction.North;
Console.WriteLine(d);        // North
Console.WriteLine((int)d);   // 0 - first member defaults to 0
By default, enum members are numbered from 0. The enum name and its members are accessed with dot notation. An enum variable has the type safety of the named type, not a raw integer.

8.2 Underlying Type and Explicit Values

enum HttpStatus : int
{
    Ok = 200,
    Created = 201,
    BadRequest = 400,
    NotFound = 404,
    ServerError = 500
}

HttpStatus status = HttpStatus.NotFound;
Console.WriteLine(status);           // NotFound
Console.WriteLine((int)status);      // 404

// Parse from integer
HttpStatus s = (HttpStatus)200;
Console.WriteLine(s);                // Ok

// Parse from string
HttpStatus parsed = Enum.Parse<HttpStatus>("Created");
Console.WriteLine(parsed);           // Created
The underlying type defaults to int. You can specify byte, short, long, or any other integral type after the colon.

8.3 [Flags] Enums

Apply [Flags] when members represent bits in a bit field. Assign powers of two as values. Combine and test members with bitwise operators:
[Flags]
enum Permission
{
    None    = 0,
    Read    = 1,
    Write   = 2,
    Execute = 4,
    All     = Read | Write | Execute
}

Permission p = Permission.Read | Permission.Write;
Console.WriteLine(p);                            // Read, Write
Console.WriteLine(p.HasFlag(Permission.Read));   // True
Console.WriteLine(p.HasFlag(Permission.Execute));// False

p |= Permission.Execute;
Console.WriteLine(p);  // Read, Write, Execute

8.4 Enum Utility Methods

// Get all values
foreach (Direction d in Enum.GetValues<Direction>())
    Console.Write($"{d} ");   // North South East West

// Get all names
string[] names = Enum.GetNames<Direction>();

// Check if a value is defined
bool valid = Enum.IsDefined(typeof(Direction), "North");  // true
bool bad   = Enum.IsDefined(typeof(Direction), "Up");     // false

// TryParse - safer than Parse
if (Enum.TryParse<Direction>("East", out Direction result))
    Console.WriteLine(result);  // East

8.5 switch with Enums

Switch expressions are the cleanest way to handle all enum cases. The compiler warns if a case is missing:
Direction dir = Direction.West;

string label = dir switch
{
    Direction.North => "up",
    Direction.South => "down",
    Direction.East  => "right",
    Direction.West  => "left",
    _ => throw new ArgumentOutOfRangeException()
};
Console.WriteLine(label);  // left

8.6 Example - All Together

// Enums - traffic light state machine with switch expression.

TrafficLight light = TrafficLight.Red;

for (int i = 0; i < 6; i++)
{
    Console.WriteLine(Describe(light));
    light = Next(light);
}

static string Describe(TrafficLight l) => l switch
{
    TrafficLight.Red    => "STOP",
    TrafficLight.Yellow => "CAUTION",
    TrafficLight.Green  => "GO",
    _ => "UNKNOWN"
};

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

enum TrafficLight { Red, Yellow, Green }
Expected output:
STOP
GO
CAUTION
STOP
GO
CAUTION

8.7 Exercise

Exercise
  • Define a Season enum with four values. Write a switch expression that maps each season to a weather description string.
  • Define a [Flags] enum DayOfWeek (not the built-in one) with values for each day. Create a Permission value representing a Mon-Wed-Fri schedule and test each day with HasFlag.
  • Use Enum.GetValues<Season>() to iterate and print all seasons with their integer value.

8.8 Common Mistakes

Casting an arbitrary int to an enum without validation

Direction d = (Direction)99;   // no exception - d holds 99
Console.WriteLine(d);           // 99 - not a named value!
Always validate with Enum.IsDefined or Enum.TryParse before casting from an external integer.

Forgetting powers of two in [Flags]

[Flags]
enum Bad { A = 1, B = 2, C = 3 }  // C = 3 = A|B, not a distinct bit!
Every distinct bit flag must be a power of two (1, 2, 4, 8, 16…). The value 3 overlaps with the combination of 1 and 2.

Using int instead of enum in APIs

Accepting a raw int where an enum is appropriate loses type safety and documentation. Callers must know valid values out of band. Use the enum type in method signatures.

8.9 Key Terms

TermMeaning
enumNamed set of integer constants; provides type-safe symbolic names
underlying typeThe integral type storing enum values; defaults to int
[Flags]Attribute enabling bitwise combination of enum members
HasFlagTests whether a flags enum value includes a specific flag
Enum.GetValues<T>Returns all declared values of an enum type
Enum.TryParse<T>Parses a string to an enum value without throwing on failure
Enum.IsDefinedTests whether a value is a declared member of an enum