switch

When one value decides between many choices, a switch is clearer than a long else if chain. C# has two forms: the classic switch statement, which runs code for a matching case, and the newer switch expression, which produces a value. Both can match patterns, not just constants.

The switch statement

C#
int day = 3;

switch (day)
{
    case 1:
        Console.WriteLine("Monday");
        break;
    case 2:
        Console.WriteLine("Tuesday");
        break;
    case 3:
        Console.WriteLine("Wednesday");
        break;
    default:
        Console.WriteLine("Another day");
        break;
}
Output
Wednesday

The value in parentheses is compared with each case. default handles anything that didn't match; it's optional, but usually a good idea.

No fall-through

In C and Java, forgetting break lets execution "fall through" into the next case — a classic bug. C# makes it a compile error: every section must end with break, return, throw or similar.

C#
int level = 1;
switch (level)
{
    case 1:
        Console.WriteLine("Beginner");
    case 2:
        Console.WriteLine("Intermediate");
        break;
}
Build error
Program.cs(4,5): error CS0163: Control cannot fall through from one case label ('case 1:') to another

To share code between values, stack the labels with nothing between them:

C#
string month = "Feb";

switch (month)
{
    case "Dec":
    case "Jan":
    case "Feb":
        Console.WriteLine("Winter");
        break;
    case "Jun":
    case "Jul":
    case "Aug":
        Console.WriteLine("Summer");
        break;
    default:
        Console.WriteLine("Spring or autumn");
        break;
}
Output
Winter

As this shows, switch works with strings as well as numbers, characters and enums.

Patterns and when guards

A case can match a pattern rather than a single value — for example a range — and a when clause adds an extra condition:

C#
int[] temperatures = [-5, 8, 19, 27, 38];

foreach (int t in temperatures)
{
    switch (t)
    {
        case < 0:
            Console.WriteLine($"{t}: freezing");
            break;
        case >= 0 and < 15:
            Console.WriteLine($"{t}: cold");
            break;
        case < 25:
            Console.WriteLine($"{t}: mild");
            break;
        case var hot when hot > 35:
            Console.WriteLine($"{t}: dangerously hot");
            break;
        default:
            Console.WriteLine($"{t}: warm");
            break;
    }
}
Output
-5: freezing
8: cold
19: mild
27: warm
38: dangerously hot

Cases are tested from top to bottom, so case < 25 only sees values that weren't already caught.

switch expressions

Often each case just produces a value. A switch expression says that directly: the value comes first, then switch, then pattern => result arms separated by commas. The underscore _ (the discard) matches anything:

C#
int day = 6;

string kind = day switch
{
    6 or 7 => "weekend",
    >= 1 and <= 5 => "weekday",
    _ => "invalid day",
};

Console.WriteLine(kind);
Output
weekend

Switch expressions are compact, and because they produce a value they work anywhere an expression does — in a return, an argument or string interpolation:

C#
foreach (decimal amount in new[] { 12m, 75m, 240m })
{
    decimal discount = amount switch
    {
        >= 200 => 0.15m,
        >= 50 => 0.05m,
        _ => 0m,
    };
    Console.WriteLine($"{amount}: discount {discount:P0}, pay {amount * (1 - discount)}");
}
Output
12: discount 0%, pay 12
75: discount 5%, pay 71.25
240: discount 15%, pay 204.00

Notice that decimal results keep the number of decimal places produced by the calculation (204.00, but 12). Lesson 18 shows how to format output to a fixed number of places.

Missing cases

A switch expression must produce a value for every input. If the compiler sees inputs that no arm handles, it warns you — and at run time an unmatched value throws an exception that reports the value:

C#
int code = 3;
string name = code switch
{
    1 => "one",
    2 => "two",
};
Console.WriteLine(name);
Output
Program.cs(2,20): warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '0' is not covered.
Unhandled exception. System.Runtime.CompilerServices.SwitchExpressionException: Non-exhaustive switch expression failed to match its input.
Unmatched value was 3.

Add a _ => arm (or one that throws a clear exception) to fix it.

Switching on several values

Wrap several values in a tuple to switch on all of them at once:

C#
string Play(string a, string b) => (a, b) switch
{
    _ when a == b => "draw",
    ("rock", "scissors") or ("scissors", "paper") or ("paper", "rock") => "player 1 wins",
    _ => "player 2 wins",
};

Console.WriteLine(Play("rock", "scissors"));
Console.WriteLine(Play("rock", "paper"));
Console.WriteLine(Play("paper", "paper"));
Output
player 1 wins
player 2 wins
draw

Which form to use

  • switch expression — when each case computes a value. This is the modern default.
  • switch statement — when cases run several statements or have side effects like printing and saving.
  • if / else if — when the conditions test different variables or unrelated things.

Try it yourself

Turn an HTTP status code into a description. Add a case for 404 and a range for server errors (500–599).

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
200: OK
301: Redirect
404: Client error
503: Unknown
999: Unknown