100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
C#

Switch Statements and Expressions

Learn how C# branches on a value using the classic switch statement and the modern, expression-based switch introduced in C# 8, including pattern matching arms.

Control FlowBeginner8 min readJul 9, 2026
Analogies

Switch Statements and Expressions

A switch is a control-flow construct that compares a single value against a series of candidate patterns and executes the code associated with the first match. C# has offered a switch statement since version 1.0, modeled loosely on C/C++ syntax, but it has evolved dramatically. Modern C# adds the switch expression (C# 8), which treats switching as something that produces a value rather than something that merely branches, and it layers in pattern matching so a switch can test types, shapes, ranges, and relational conditions instead of only exact equality. Understanding both forms — and when to reach for each — is essential because switches appear constantly in parsing, state machines, and business-rule code.

🏏

Cricket analogy: A switch judging the delivery type, over-pitched, yorker, or bouncer, and executing a different response for the first match, is like the old switch statement, while a switch expression is more like a scorer instantly producing a run value for that ball type.

The classic switch statement

The switch statement evaluates an expression once and jumps to the matching case label. Each case must end with a jump statement (typically break, but return, continue, or goto case are also valid) — C# deliberately disallows the implicit fall-through that plagues C, so you cannot accidentally execute the next case's body. Multiple case labels can share one block by stacking them, and a default label handles anything unmatched. Because it is a statement, it produces no value of its own; you assign inside each branch or return early.

🏏

Cricket analogy: Each case in a switch statement needing an explicit break, like an umpire, is deliberate: C# won't let the scorer accidentally carry a 'wide' ruling into the next ball's case the way old scoring rules sometimes blurred together.

csharp
int score = 82;
string grade;

switch (score)
{
    case >= 90:
        grade = "A";
        break;
    case >= 80:
        grade = "B";
        break;
    case >= 70:
        grade = "C";
        break;
    default:
        grade = "F";
        break;
}

Console.WriteLine(grade); // "B"

The switch expression

The switch expression, introduced in C# 8, flips the syntax around: the switched-on value comes first, each arm uses '=>' instead of 'case ... :', arms are separated by commas, and the whole construct evaluates to a value. There is no break, no fall-through to worry about, and the compiler enforces exhaustiveness — if you omit a discard arm ('_') and the compiler cannot prove every case is covered, you get a warning. This makes switch expressions ideal for computing a result concisely, especially when combined with pattern matching over types or tuples.

🏏

Cricket analogy: A switch expression computing runsScored = deliveryOutcome switch { Boundary => 4, Six => 6, _ => 0 } directly yields the runs to add to the total, with the compiler warning if some outcome like 'no-ball' was left uncovered.

csharp
string Describe(object shape) => shape switch
{
    Circle { Radius: <= 0 } => "invalid circle",
    Circle c => $"circle with area {Math.PI * c.Radius * c.Radius:F2}",
    Rectangle { Width: var w, Height: var h } when w == h => "square",
    Rectangle r => $"rectangle {r.Width}x{r.Height}",
    null => "nothing supplied",
    _ => "unknown shape"
};

record Circle(double Radius);
record Rectangle(double Width, double Height);

Pattern matching in switch arms

Both switch forms support rich patterns: constant patterns (exact values), type patterns ('Circle c'), relational patterns ('>= 90'), property patterns ('{ Radius: <= 0 }'), tuple patterns, and combinators ('and', 'or', 'not'). A 'when' clause adds an arbitrary boolean guard for conditions patterns cannot express directly, such as comparing two properties to each other. Patterns are evaluated top to bottom, and the first arm whose pattern matches — and whose guard, if any, is true — wins, so order matters when patterns overlap.

🏏

Cricket analogy: A property pattern like { Overs: >= 50 } combined with a when clause comparing runsScored to wicketsLost is like an analyst flagging a genuine batting collapse only when both the run rate and wicket count line up, not either alone.

Unlike Java's switch (pre-Java 21) or C's switch, which only test equality against constants, C#'s pattern-matching switch can test an object's runtime type and destructure its properties in the same arm — closer to what Scala or Kotlin's 'when' expressions offer.

A switch expression throws a SwitchExpressionException at runtime if no arm matches and there's no discard pattern — this is a runtime failure, not a compile error, even though the compiler warns you about non-exhaustive coverage. Always include a '_' arm unless you are certain every case is a compile-time-verifiable enum or bool value.

  • The switch statement uses 'case:' labels and requires an explicit jump (break/return) per branch — no implicit fall-through.
  • The switch expression (C# 8+) uses 'value switch { pattern => result }' and always produces a value.
  • Switch expressions support type patterns, property patterns, relational patterns, and 'when' guards.
  • Arms are tested top-to-bottom; put more specific patterns before more general ones.
  • Missing coverage in a switch expression compiles with a warning but throws SwitchExpressionException at runtime.
  • Prefer switch expressions for computing a value concisely; use switch statements when each branch needs multiple imperative steps.

Practice what you learned

Was this page helpful?

Topics covered

#CStudyNotes#Programming#SwitchStatementsAndExpressions#Switch#Statements#Expressions#Classic#StudyNotes#SkillVeris#ExamPrep