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.
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.
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
1. What happens if a switch expression has no matching arm and no discard pattern?
2. Which syntax element separates arms in a switch expression?
3. In a switch statement, what does C# require instead of allowing implicit fall-through?
4. What does a 'when' clause add to a switch pattern?
5. Given overlapping patterns in a switch expression, how does C# decide which arm executes?
Was this page helpful?
You May Also Like
Conditional Statements
Explains how C# branches program flow using if/else, the ternary conditional operator, and pattern-based conditions, with guidance on readability and pitfalls.
Pattern Matching
Survey C#'s pattern matching features — type patterns, property patterns, relational and logical patterns, and switch expressions — for expressive, safe branching.
Loops in C#
Covers C#'s four looping constructs — for, while, do-while, and foreach — along with break, continue, and guidance on choosing the right loop for the job.
Classes and Objects
Understand how C# classes define blueprints for reference-type objects, covering fields, methods, object creation, and the distinction between a class and its instances.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics