What is pattern matching in C#?

C# Language Features Intermediate 2–3 Yrs3–5 Yrs Conceptual 2 min read

Quick Answer

Pattern matching tests a value against a shape and (optionally) extracts data from it in one expression. It covers type patterns (o is Customer c), constant patterns, relational (> 100), logical (and/or/not), property patterns ({ Status: Active }), list patterns and tuple patterns — usable in is, switch statements and switch expressions.

Pattern matching lets me check what a value is and pull data out of it in a single expression, instead of a cast plus a null check plus an if. The common ones are the type pattern — o is Customer c gives me a typed c if it matches — and the switch expression, which returns a value and gets compiler warnings if I miss a case. There are also property patterns like { Status: Active, Total: > 1000 }, and relational and logical patterns with and, or, not. It makes branching code much cleaner, though for genuinely polymorphic behaviour a virtual method can still be the better choice.

It replaces long if/else chains and casts with declarative code:

  • Type + declaration: if (shape is Circle c) return Math.PI * c.R * c.R;
  • switch expression: returns a value, is exhaustive-checked by the compiler.
  • Property pattern: order is { Total: > 1000, Customer.IsVip: true }.
  • Relational + logical: temp switch { < 0 => "freezing", >= 0 and < 30 => "mild", _ => "hot" }.
  • when clause adds an arbitrary condition to a case.

Benefits: fewer casts, the compiler warns about unhandled cases in switch expressions, and the intent is clearer. Overuse on polymorphic types can be a smell — sometimes a virtual method is better.

C# — switch expression
decimal Fee(Account a) => a switch
{
    { IsVip: true }                 => 0m,
    { Balance: >= 10_000 }           => 2m,
    { Balance: >= 0 and < 10_000 }   => 5m,
    _                               => throw new ArgumentException("invalid")
};