Pattern Matching in C#
Overview
Pattern matching in C# allows you to check a value against a pattern and, when it matches, extract information from that value in a concise and readable way.
It extends the power of traditional conditionals (if, switch) by combining:
- Type checking
- null checking
- value deconstruction
into one clean syntax.
In simple terms:
Pattern matching lets you say “if this object fits this shape, use it” — in a single, readable expression.
Core Concept Example
Traditional approach
if (obj is Customer)
{
var c = (Customer)obj;
Console.WriteLine(c.Name);
}
Pattern matching approach
if (obj is Customer c)
{
Console.WriteLine(c.Name);
}
- More concise
- Type-safe — no need for a cast
- Readable and expressive
Types of Patterns in C#
1. Type Patterns
Used to check type and extract the object.
if (shape is Circle c)
Console.WriteLine($"Radius: {c.Radius}");
2. Constant Patterns
Check if a value equals a constant.
if (statusCode is 200)
Console.WriteLine("OK");
else if (statusCode is 404)
Console.WriteLine("Not Found");
3. Relational Patterns
Compare values using relational operators (<, >, <=, >=).
if (temperature is > 30)
Console.WriteLine("Too hot!");
4. Logical Patterns (and, or, not)
Combine multiple patterns logically.
if (age is >= 18 and < 65)
Console.WriteLine("Working-age adult");
if (value is not null)
Console.WriteLine("Value is set");
5. Property Patterns
Match object properties directly.
if (person is { Name: "Alice", Age: >= 18 })
Console.WriteLine("Adult named Alice");
You can also nest patterns:
if (order is { Customer.Address.Country: "UK" })
Console.WriteLine("British order");
6. Tuple and Positional Patterns
Match tuples or deconstructed objects.
(string country, int population) = ("UK", 68);
if ((country, population) is ("UK", > 60))
Console.WriteLine("Large European country");
With deconstruction support:
if (point is (0, 0))
Console.WriteLine("Origin");
7. Switch Expressions with Patterns (C# 8+)
The modern switch syntax is built around pattern matching:
string GetWeatherAdvice(int temperature) => temperature switch
{
< 0 => "Freezing",
< 15 => "Cold",
< 25 => "Mild",
< 35 => "Warm",
_ => "Hot"
};
This is:
- Cleaner than nested ifs
- More readable
- Easy to extend
Why Pattern Matching Is Good
| Benefit | Description |
|---|---|
| Readability | Replaces verbose type-checking and casting logic. |
| Safety | Eliminates runtime casting errors — compiler ensures correctness. |
| Conciseness | Fewer lines of code and reduced boilerplate. |
| Declarative style | Focuses on what you’re checking, not how. |
| Better maintainability | Easy to extend complex branching logic. |
Real-World Example: Message Handling
object message = new OrderPlaced("1234");
switch (message)
{
case OrderPlaced o:
ProcessOrder(o.Id);
break;
case OrderCancelled c:
CancelOrder(c.Id);
break;
default:
LogUnknown(message);
break;
}
Here, pattern matching cleanly replaces multiple if/is checks — common in event-driven systems.
Drawbacks and Considerations
| Drawback | Explanation |
|---|---|
| Overuse can reduce clarity | Deeply nested property patterns can become hard to read. |
| Performance overhead | Each match can introduce small runtime checks (usually negligible). |
| Complex switch expressions | Large pattern-based switches may be harder for new developers to follow. |
| Not ideal for dynamic types | Pattern matching relies on static typing — dynamic objects need extra handling. |
Example Summary (Compact Form)
public string Describe(object o) => o switch
{
int i => $"Integer: {i}",
string s => $"String: '{s}'",
null => "Null value",
{ } obj => $"Some object of type {obj.GetType().Name}",
_ => "Unknown"
};
Summary
Pattern matching in C# allows you to check a variable’s type, value, or structure and extract data in one expression. It’s used to simplify type checks, null checks, and conditional logic — making code more readable and type-safe. It’s especially powerful in modern switch expressions and with records or deconstructed tuples. It should be used to simplify code, but avoided when patterns become overly complex or nested.