Pattern Matching · TL;DR
1 min readTL;DR
A pattern is a test you apply to a value: "is it a Circle?", "is it null?", "is it between 0 and 100?", "does it have a Country of "CY"?". C# lets you use patterns in three places: the is expression (if (shape is Circle c)), the switch statement (case Circle c when c.Radius > 10:) and the switch expression (shape switch { Circle c => ..., _ => ... }). The pattern kinds arrived in waves: type/declaration, constant and var patterns in C# 7.0; the switch expression, property, positional and tuple patterns in C# 8; relational (< 0), logical (and, or, not) and parenthesised patterns plus the plain type pattern in C# 9; extended property patterns ({ Address.City: "Nicosia" }) in C# 10; list and slice patterns ([1, .., var last]) in C# 11. The compiler turns a pattern into ordinary type checks (isinst), comparisons and property reads, with no allocation, and checks a switch expression for exhaustiveness (warning CS8509 if an input can fall through, which throws SwitchExpressionException at runtime). Traps: is null is not the same as == null when == is overloaded, a constant pattern against an object boxes nothing but a var or discard pattern matches null, and a when guard turns off the compiler's ability to prove exhaustiveness for that arm.