Pattern matching and switch expressions

C# · Practical · 12 min

Patterns replace long if/else chains and casts with concise, compiler-checked branching.

Pattern matching tests a value against a shape and can extract parts of it. It works with is and with switch expressions.

var label = shape switch { Circle c => ..., _ => "?" };

Pattern kinds:

  • Type pattern with designation: obj is string s tests and casts in one step.
  • Constant: x is null, status is "ok".
  • Relational: < 0, >= 18; combine with and, or, not: n is > 0 and < 10.
  • Property pattern: order is { Status: "paid", Total: > 100 }, nested as deep as you like.
  • Positional pattern on types with Deconstruct (records): Point(0, 0).
  • List patterns (C# 11): [1, .., var last], [], [_, _].
  • Discard _ matches anything; var x matches and captures.

A switch expression is an expression that must produce a value; arms are tried top to bottom and _ is the fallback. The compiler warns when the arms are not exhaustive and errors when an arm can never be reached, which is valuable when combined with enums or closed hierarchies.

Use switch expressions for mapping inputs to results (pricing rules, state transitions, parsing tokens). Keep complex logic in methods and patterns for the decision itself.

Example

static string Describe(object o) => o switch
{
    null => "null",
    int n when n < 0 => "negative int",
    int n and > 100 => $"big int {n}",
    int => "small int",
    string { Length: 0 } => "empty string",
    string s => $"string '{s}'",
    Order { Status: "paid", Total: > 100 } => "big paid order",
    Order { Items: [var only] } => $"order of one {only}",
    Order { Items: [var first, ..] } => $"order starting with {first}",
    _ => "something else",
};

Console.WriteLine(Describe(250));       // big int 250
Console.WriteLine(Describe(-5));        // negative int
Console.WriteLine(Describe(""));        // empty string
Console.WriteLine(Describe(new Order("paid", 500m, ["tv"])));    // big paid order
Console.WriteLine(Describe(new Order("new", 5m, ["pen"])));      // order of one pen
Console.WriteLine(Describe(new Order("new", 9m, ["a", "b"])));   // order starting with a
Console.WriteLine(Describe(3.5));       // something else

record Order(string Status, decimal Total, string[] Items);

Remember

  • obj is T t tests and casts in one step
  • Property, relational and list patterns describe data shapes declaratively
  • Switch expressions return a value; arms are checked top to bottom
  • The compiler flags non-exhaustive and unreachable arms

Watch out for

  • Putting a broad arm (like string s) before a more specific one, which makes the latter unreachable.

Check yourself

  1. What does 7 is > 0 and < 5 evaluate to?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: learn.microsoft.com/en-us/dotnet/csharp/fundamentals/functional/pattern-matching

Builds on

Unlocks

Nothing builds on this yet.