Switch Expressions vs Switch Statements in C# 8

C# 8 introduced switch expressions, providing a concise, expression-based alternative to the traditional switch statement. While traditional switch statements execute blocks of procedural code based on matching conditions, switch expressions evaluate patterns to produce a single value directly. This article compares how switch expressions work against traditional switch statements, highlighting key syntax changes, functional differences, and best practices for writing cleaner, more expressive C# code.

Core Differences: Expressions vs. Statements

The fundamental difference lies in their language classification:

  • Switch Statement: A control-flow construct that executes code blocks. It does not inherently return a value, often requiring variable mutation inside cases and manual flow control via break, return, or goto.
  • Switch Expression: An expression that yields a value directly. It is assigned to a variable or returned directly from a method, eliminating boilerplate and intermediate state assignment.

Syntax Comparison

Traditional switch statements require repetitive keywords like case, break, and default. Switch expressions streamline this using lambda-like arrows (=>) and comma-separated arms.

Traditional Switch Statement (C# 7 and earlier)

public string GetDayType(DayOfWeek day)
{
    string result;
    switch (day)
    {
        case DayOfWeek.Saturday:
        case DayOfWeek.Sunday:
            result = "Weekend";
            break;
        case DayOfWeek.Monday:
        case DayOfWeek.Tuesday:
        case DayOfWeek.Wednesday:
        case DayOfWeek.Thursday:
        case DayOfWeek.Friday:
            result = "Weekday";
            break;
        default:
            throw new ArgumentOutOfRangeException(nameof(day), "Invalid day");
    }
    return result;
}

C# 8 Switch Expression

public string GetDayType(DayOfWeek day) => day switch
{
    DayOfWeek.Saturday or DayOfWeek.Sunday => "Weekend",
    DayOfWeek.Monday or DayOfWeek.Tuesday or DayOfWeek.Wednesday 
        or DayOfWeek.Thursday or DayOfWeek.Friday => "Weekday",
    _ => throw new ArgumentOutOfRangeException(nameof(day), "Invalid day")
};

Key Functional Changes

1. Inverted Target Syntax

In a traditional statement, the keyword precedes the expression being evaluated: switch (target). In a switch expression, the target precedes the keyword: target switch { ... }.

2. The Discard Pattern (_) Replaces default

Traditional switches use the default: label as a catch-all. Switch expressions use the underscore discard pattern (_ => ...) to handle any unmatched cases.

3. Exhaustiveness Checking

Because switch expressions produce a value, the compiler enforces exhaustiveness. If a switch expression does not cover all possible input values and lacks a discard (_) case, the compiler generates a warning. At runtime, an unhandled value throws an InvalidOperationException (specifically, a SwitchExpressionException). Traditional switch statements simply fall through without warning if cases are unhandled.

4. Advanced Pattern Matching

Switch expressions integrate cleanly with C# pattern matching, allowing property matching, tuple matching, and positional matching with minimal syntax:

public decimal CalculateDiscount(Order order) => order switch
{
    { IsPremiumCustomer: true, TotalAmount: > 100m } => 0.20m,
    { IsPremiumCustomer: true } => 0.10m,
    { TotalAmount: > 100m } => 0.05m,
    _ => 0m
};

When to Use Which

  • Use Switch Expressions when your goal is to evaluate input data and map it directly to a single value, object, or result. They are ideal for pure functions, data transformations, and state-to-value mappings.
  • Use Switch Statements when you need to execute multi-step operations, trigger side effects, perform complex logging per case, or handle asynchronous calls using await within individual branches.