C# Switch Statement vs Switch Expression: Which to Use?
c# switch statement vs switch expression: Compare the classic C# switch statement with the switch expression: syntax, behavior, pattern matching, and when each fits best.
When you need to branch on a value in C#, you have two main options: the switch statement, available since C# 1.0, and the switch expression, introduced in C# 8.0. The c# switch statement vs switch expression decision comes down to syntax, return value, and pattern matching flexibility. This article compares both forms and gives practical guidance on when to use each.
The Traditional switch Statement
The switch statement evaluates a single expression and executes the first matching case block. It has been part of C# since the beginning and is familiar to most developers.
public static string GetDayName(int day) { switch (day) { case 1: return "Monday"; case 2: return "Tuesday"; case 3: return "Wednesday"; default: return "Unknown"; } }
Each case must end with a break, return, throw, or another jump statement to prevent fall-through. The default case handles any value that does not match a specific case. The statement is useful when you need to execute multiple statements per branch or when you want to use goto to jump between cases, though goto is rarely needed in practice.
The switch Expression
The switch expression, introduced in C# 8, is a more concise, expression-based alternative. It produces a value and uses arrow syntax (=>) to map each case to an expression.
public static string GetDayName(int day) => day switch { 1 => "Monday", 2 => "Tuesday", 3 => "Wednesday", _ => "Unknown" };
The underscore (_) is the discard pattern, equivalent to default. The switch expression must be exhaustive: the compiler must be able to prove that every possible input is covered. If you omit the discard and the input does not match any case, a SwitchExpressionException is thrown at runtime.
Key Differences in Syntax and Behavior
| Aspect | switch Statement | switch Expression |
|---|---|---|
| Introduced in | C# .0 | C# 8.0 |
| Returns a value | No, uses return or assignment | Yes, directly produces a value |
| Case syntax | case value: | value => |
| Fall-through | Allowed with goto; otherwise requires jump | Not allowed; each arm is an expression |
| Default case | default: | _ discard pattern |
| Exhaustiveness check | Not enforced | Enforced by compiler |
| Multiple statements | Yes, in each case block | No, each arm is a single expression |
These differences affect how you structure code. The switch expression is more compact and often reads better when each branch is a simple expression. The switch statement remains necessary when you need to run multiple statements or use break to exit early.
Pattern Matching Capabilities
Both forms support pattern matching, but the switch expression extends it with a cleaner syntax. For example, you can match on type patterns, property patterns, and relational patterns.
public static string Describe(object obj) => obj switch { int i when i > 0 => "Positive integer", int i => "Non-positive integer", string s => $"String of length {s.Length}", _ => "Unknown type" };
The switch statement can also use patterns, but the syntax is more verbose:
switch (obj) { case int i when i > 0: return "Positive integer"; case int i: return "Non-positive integer"; case string s: return $"String of length {s.Length}"; default: return "Unknown type"; }
For complex pattern matching, the switch expression tends to be more readable because it avoids repeated case and return keywords. However, if a branch requires multiple statements, the switch statement is the only option.
When to Use Each
Choose the switch expression when:
- Each branch is a single expression that returns a value.
- You want to assign the result directly to a variable or return it.
- You are using pattern matching and want concise syntax.
- You want the compiler to enforce exhaustiveness.
Choose the switch statement when:
- You need to execute multiple statements per branch.
- You need to use
breakto exit a loop orgototo jump to another case. - You are working with code that must remain compatible with C# versions before 8.0.
- You have a large number of cases and prefer the traditional
caseblock structure for clarity.
There is no universal winner. The decision depends on the shape of the logic you are implementing.
Performance and Compilation Behavior
Both the switch statement and the switch expression compile to similar intermediate language (IL). The compiler may generate a jump table for contiguous integer cases or a series of comparisons for other types. There is no inherent performance advantage to either form; the JIT compiler optimizes both based on the same underlying pattern.
One subtle difference is that the switch expression is an expression, so it can be used inline in a larger expression without an intermediate variable. This can reduce allocation and improve readability in hot paths, but the runtime cost is negligible in most scenarios. If you are micro-optimizing, measure the actual impact rather than assuming one form is faster.
The main performance consideration is pattern matching complexity. For example, matching a type pattern requires a type check, which is fast but not free. The compiler may optimize simple constant patterns into a jump table, but complex patterns are evaluated sequentially. Keep this in mind when writing performance-sensitive code, regardless of which syntax you choose.
Common Pitfalls and Edge Cases
The switch expression's exhaustiveness requirement can cause unexpected exceptions. If you omit the discard pattern and the input does not match any arm, the runtime throws SwitchExpressionException. This is a deliberate design choice to force you to handle all cases. In contrast, the switch statement silently falls through to the default case if one exists, or simply does nothing if there is no default.
Another pitfall is the switch expression's inability to execute multiple statements. You might be tempted to use a block expression, but C# does not allow that. If you need side effects beyond returning a value, use the switch statement or refactor the logic into a helper method.
Fall-through is a common source of bugs in switch statements. C# requires an explicit jump statement at the end of each case, but it still allows goto case to intentionally fall through. This is rarely used and can make code harder to follow. The switch expression eliminates this problem entirely because each arm is an expression and there is no concept of fall-through.
Compatibility and Language Version
The switch expression requires C# 8.0 or later. If you are targeting .NET Framework or an older .NET Core version, you may not have access to it. The switch statement works in all C# versions, making it the safer choice for legacy codebases.
When migrating to a newer C# version, you can gradually replace switch statements with switch expressions where the logic is simple. The compiler will help you identify missing cases through exhaustiveness analysis. However, be aware that the switch expression is not always a drop-in replacement; it changes the control flow from statement-based to expression-based, which may require restructuring code that relies on break or goto.
For teams using the latest .NET versions, the switch expression is a valuable tool for writing concise, expressive branching logic. It pairs well with other C# 8 features like nullable reference types and using declarations. The choice between the two forms should be guided by the complexity of the branch logic and the need for exhaustiveness, not by habit or preference.