Pattern Matching

C# / .NET Interview Notes


1. Big Picture

Pattern matching in C# lets you inspect data and make decisions based on its shape, type, or value — all in a concise expression. It evolved from simple switch statements into a powerful feature spanning is, switch expressions, and property/positional patterns.

⭐ Quick Memory Trick:
is → "Are you this type?" (type check + cast)
switch → "Which one are you?" (multi-way branch)
when → "Only if this is also true" (guard clause)

2. Type Pattern (is)

The simplest pattern — checks if an object is a type and casts it in one step:

object obj = "hello";

// Old way
if (obj is string)
{
    string s = (string)obj;
    Console.WriteLine(s.Length);
}

// Pattern matching way
if (obj is string s)
{
    Console.WriteLine(s.Length);  // s is already string
}

Common Type Patterns

if (obj is int n) { /* n is int */ }
if (obj is string s) { /* s is string */ }
if (obj is not null) { /* obj is not null */ }
if (obj is null) { /* obj is null */ }

3. Switch Expression

A more expressive replacement for switch statements — returns a value:

string DayName(int day) => day switch
{
    1 => "Monday",
    2 => "Tuesday",
    3 => "Wednesday",
    4 => "Thursday",
    5 => "Friday",
    6 or 7 => "Weekend",
    _ => "Invalid"   // default
};
💡 Tip: Switch expressions use => and are exhaustive — the compiler warns if you miss a case.

4. Property Pattern

Match on the properties of an object, not just its type:

record Person(string Name, int Age, string City);

var p = new Person("Alice", 30, "NYC");

// Match on property values
if (p is { Age: > 18, City: "NYC" })
{
    Console.WriteLine("Adult in NYC");
}

// Match on nested properties
if (p is { Name.Length: > 3 })
{
    Console.WriteLine("Long name");
}

Property Pattern Syntax

PatternMeaning
{ Age: > 18 }Age greater than 18
{ Name: "Alice" }Name equals "Alice"
{ City: not null }City is not null
{ Age: >= 18 and <= 65 }Age in range
{ Name.Length: > 3 }Nested property access

5. Positional Pattern

Deconstruct an object and match on its positional values:

record Point(int X, int Y);

var pt = new Point(3, 4);

// Positional deconstruction
if (pt is ( > 0, > 0 ))
{
    Console.WriteLine("First quadrant");
}

// With variable binding
if (pt is (var x, var y) && x == y)
{
    Console.WriteLine("On diagonal");
}

6. when Guard Clause

Add extra conditions to any pattern:

string Classify(int n) => n switch
{
    > 0 when n % 2 == 0 => "Positive even",
    > 0 when n % 2 != 0 => "Positive odd",
    < 0 => "Negative",
    0 => "Zero",
    _ => "Unknown"
};

7. and / or / not Combinators

Combine patterns with logical operators:

int age = 25;

// and
if (age is > 18 and < 65) { /* working age */ }

// or
if (age is < 13 or > 65) { /* dependent */ }

// not
if (age is not 0) { /* non-zero */ }

// Complex combination
if (age is > 18 and < 65 or 100) { /* 18-65 or exactly 100 */ }

8. List Patterns (C# 11+)

Match on the structure of arrays and lists:

int[] nums = { 1, 2, 3 };

if (nums is [1, 2, 3]) { /* exact match */ }
if (nums is [1, _, _]) { /* starts with 1 */ }
if (nums is [_, _, 3]) { /* ends with 3 */ }
if (nums is [1, .., 3]) { /* starts 1, ends 3 */ }
if (nums is [.., 2, ..]) { /* contains 2 anywhere */ }
if (nums is []) { /* empty */ }
if (nums is not []) { /* not empty */ }
⚠️ Note: List patterns require C# 11 / .NET 7+. The .. is a slice pattern — [1, .., 3] means "starts with 1, ends with 3, anything in between."

9. Relational Patterns

Compare values directly in patterns:

int temp = 25;

string Feel(int t) => t switch
{
    < 0 => "Freezing",
    >= 0 and < 15 => "Cold",
    >= 15 and < 25 => "Mild",
    >= 25 and < 35 => "Warm",
    >= 35 => "Hot"
};

10. Practical Examples

Example 1: Parsing Input

object ParseInput(object input) => input switch
{
    int n when n > 0 => $"Positive number: {n}",
    string s when s.Length > 0 => $"Non-empty string: {s}",
    null => "Null input",
    _ => "Unknown type"
};

Example 2: State Machine

enum State { Idle, Running, Paused, Stopped }

string HandleCommand(State current, string cmd) => (current, cmd) switch
{
    (State.Idle, "start") => "Started",
    (State.Running, "pause") => "Paused",
    (State.Running, "stop") => "Stopped",
    (State.Paused, "resume") => "Resumed",
    (State.Paused, "stop") => "Stopped",
    (State.Stopped, "reset") => "Reset to idle",
    _ => "Invalid command"
};

Example 3: Validation

record User(string Name, int Age, string Email);

bool IsValid(User u) => u switch
{
    { Name: null or "" } => false,
    { Age: < 0 or > 150 } => false,
    { Email: not null and _ } when u.Email.Contains("@") => true,
    _ => false
};

11. Pattern Matching vs Traditional

AspectTraditionalPattern Matching
Type check + castis + (T)is T t
Multi-way branchswitch statementswitch expression
Property checkMultiple if conditions{ Prop: value }
Null checkif (x != null)is not null
Collection shapeManual index checks[1, .., 3]

12. Common Interview Questions

Q: What is the difference between is and as?

is returns a bool (and with pattern matching, binds a variable). as returns the cast object or null — it never throws, but you still need a null check.

Q: Can pattern matching replace polymorphism?

Sometimes — for simple type-based dispatch. But for complex behavior, polymorphism (virtual methods) is still cleaner. Pattern matching shines for data inspection, not behavior dispatch.

Q: What is the _ (discard) pattern?

A wildcard that matches anything. Used in switch expressions as the default case, or in positional patterns to ignore specific values: (1, _, 3).

Q: Are switch expressions exhaustive?

Yes — the compiler enforces it. If you omit a case, you get a compile error. Use _ as the catch-all.


13. One-Line Interview Answer

Pattern matching in C# is a set of language features that let you inspect and deconstruct data concisely — type patterns (is T t), property patterns ({ Age: > 18 }), positional patterns ((x, y)), list patterns ([1, .., 3]), and switch expressions — reducing boilerplate and making code more readable.