🎯 C# Pattern Matching: The Shape-Sorting Superpower
Imagine you have a magic sorting hat that can look at ANY object and instantly know exactly what it is, what’s inside it, and where it belongs. That’s pattern matching!
🌟 The Big Picture
Think of pattern matching like a super-smart mail sorter. When a package arrives:
- It checks the shape (Is it a box? An envelope?)
- It peeks at the label (Who sent it? What’s inside?)
- It reads the address (Where does it go?)
C# pattern matching does exactly this with your data!
📚 What We’ll Learn
graph TD A["🎁 Pattern Matching"] --> B["📦 Type Patterns"] A --> C["🏷️ Property Patterns"] A --> D["📍 Position Patterns"] A --> E["📋 List Patterns"] A --> F["🔢 Relational Patterns"] A --> G["🏗️ Object Inits"] A --> H["👻 Anonymous Types"]
1️⃣ Pattern Matching Introduction
What Is It?
Pattern matching is like playing “Guess What’s In The Box”:
- You have a mystery object
- You ask smart questions about it
- Based on answers, you do different things
The Old Way vs The New Way
Old way (checking type manually):
if (animal is Dog)
{
Dog d = (Dog)animal;
d.Bark();
}
New way (pattern matching magic):
if (animal is Dog d)
{
d.Bark();
}
One line does what took three! ✨
Where Can You Use It?
isexpressionsswitchstatementsswitchexpressions
2️⃣ Type and Declaration Patterns
The Type Pattern
This asks: “Are you THIS type of thing?”
Think of it like sorting toys:
- “Is this a LEGO piece?” → Put in LEGO box
- “Is this a doll?” → Put in doll box
object mystery = "Hello World";
if (mystery is string)
{
Console.WriteLine("It's text!");
}
The Declaration Pattern
This does TWO things at once:
- Checks the type
- Creates a new variable!
Like saying: “If this IS a dog, call it ‘buddy’”
object mystery = 42;
if (mystery is int number)
{
// 'number' is ready to use!
Console.WriteLine(number * 2); // 84
}
Using in Switch
string Describe(object obj) => obj switch
{
int n => quot;Number: {n}",
string s => quot;Text: {s}",
bool b => quot;True/False: {b}",
null => "Nothing here!",
_ => "Mystery object"
};
The _ is like saying “everything else”.
3️⃣ Property Patterns
The Idea
Property patterns let you peek INSIDE objects. Like asking:
- “Is this a person AND are they older than 18?”
- “Is this a car AND is it red?”
Basic Example
Imagine a Person:
record Person(string Name, int Age);
Now match on properties:
var person = new Person("Alex", 25);
if (person is { Age: 25 })
{
Console.WriteLine("They're 25!");
}
Nested Properties
You can go deeper!
record Address(string City);
record Person(string Name, Address Home);
var alex = new Person("Alex",
new Address("Paris"));
if (alex is { Home: { City: "Paris" } })
{
Console.WriteLine("Parisian!");
}
// Shorter syntax:
if (alex is { Home.City: "Paris" })
{
Console.WriteLine("Still Parisian!");
}
Combining Patterns
string GetDiscount(Person p) => p switch
{
{ Age: < 12 } => "Kids: 50% off",
{ Age: >= 65 } => "Seniors: 30% off",
{ Name: "VIP" } => "VIP: 20% off",
_ => "Standard price"
};
4️⃣ Positional Patterns
What Are They?
When objects can “break apart” into pieces, you can match those pieces by POSITION.
Like unpacking a gift box layer by layer!
With Tuples
Tuples are perfect for this:
var point = (3, 4);
string location = point switch
{
(0, 0) => "At the center!",
(0, _) => "On the Y-axis",
(_, 0) => "On the X-axis",
(_, _) => "Somewhere else"
};
The _ means “I don’t care about this value”.
With Deconstruct
Any class with Deconstruct works:
record Point(int X, int Y);
var p = new Point(5, 10);
if (p is (5, 10))
{
Console.WriteLine("Found it!");
}
Combining with Property Patterns
record Rectangle(Point TopLeft, Point Size);
var rect = new Rectangle(
new Point(0, 0),
new Point(100, 50)
);
if (rect is (_, (100, _)))
{
Console.WriteLine("Width is 100!");
}
5️⃣ List and Relational Patterns
List Patterns (C# 11+)
Match arrays and lists by their content!
Match exact elements:
int[] nums = { 1, 2, 3 };
if (nums is [1, 2, 3])
{
Console.WriteLine("Perfect match!");
}
Match first and last:
if (nums is [1, .., 3])
{
// Starts with 1, ends with 3
// '..' means "zero or more items"
}
Capture middle elements:
if (nums is [1, .. var middle, 3])
{
// middle = [2]
}
Match length:
if (nums is [_, _, _])
{
Console.WriteLine("Exactly 3 items!");
}
Relational Patterns
Compare with <, >, <=, >=:
string Grade(int score) => score switch
{
>= 90 => "A - Excellent!",
>= 80 => "B - Great!",
>= 70 => "C - Good",
>= 60 => "D - Pass",
_ => "F - Try again"
};
Logical Patterns
Combine with and, or, not:
string Describe(int n) => n switch
{
> 0 and < 10 => "Single digit positive",
>= 10 and < 100 => "Two digits",
< 0 or > 1000 => "Out of range",
not 42 => "Not the answer",
42 => "The answer!"
};
6️⃣ Object and Collection Initializers
Object Initializers
Create and set up objects in ONE step!
Without initializer:
var person = new Person();
person.Name = "Alex";
person.Age = 25;
With initializer:
var person = new Person
{
Name = "Alex",
Age = 25
};
Much cleaner! ✨
Nested Object Initializers
var student = new Student
{
Name = "Alex",
Address = new Address
{
City = "Paris",
Country = "France"
}
};
Collection Initializers
Create lists with items instantly:
var numbers = new List<int> { 1, 2, 3, 4, 5 };
var people = new List<Person>
{
new Person { Name = "Alex" },
new Person { Name = "Sam" }
};
Dictionary Initializers
var scores = new Dictionary<string, int>
{
["Alex"] = 95,
["Sam"] = 88,
["Jordan"] = 92
};
With Target-Typed New
Even shorter in C# 9+:
List<int> nums = new() { 1, 2, 3 };
Person p = new() { Name = "Alex" };
7️⃣ Anonymous Types
What Are They?
Quick, throwaway objects without writing a class!
Like making a sticky note instead of a formal document.
Creating Anonymous Types
var person = new
{
Name = "Alex",
Age = 25
};
Console.WriteLine(person.Name); // Alex
Console.WriteLine(person.Age); // 25
Key Points
- Read-only: You can’t change values after creation
- Type-safe: The compiler knows the properties
- Local use: Best for quick, local operations
Perfect for LINQ
var people = new List<Person> { /* ... */ };
var summary = people
.Select(p => new
{
p.Name,
IsAdult = p.Age >= 18
});
foreach (var item in summary)
{
Console.WriteLine(quot;{item.Name}: {item.IsAdult}");
}
Anonymous Types in Pattern Matching
var data = new { Type = "fruit", Name = "apple" };
// Check properties
if (data is { Type: "fruit" })
{
Console.WriteLine(quot;Found fruit: {data.Name}");
}
🎯 Quick Recap
| Pattern Type | What It Does | Example |
|---|---|---|
| Type | Checks what type | is string |
| Declaration | Checks type + creates variable | is int n |
| Property | Peeks at properties | is { Age: 25 } |
| Positional | Matches by position | is (0, 0) |
| List | Matches array content | is [1, 2, 3] |
| Relational | Compares values | >= 10 |
🚀 Why This Matters
Pattern matching makes your code:
- Cleaner - Less boilerplate
- Safer - Compiler catches mistakes
- Readable - Shows intent clearly
- Fun - Like solving puzzles!
🌈 The Magic Combo
Combine everything:
string Analyze(object obj) => obj switch
{
null => "Nothing!",
int n when n < 0 => "Negative number",
int n => quot;Number: {n}",
string { Length: 0 } => "Empty text",
string { Length: > 100 } => "Long text",
string s => quot;Text: {s}",
int[] { Length: 0 } => "Empty array",
int[] [var first, .., var last]
=> quot;From {first} to {last}",
Person { Age: >= 18 } p => quot;Adult: {p.Name}",
Person p => quot;Minor: {p.Name}",
_ => "Unknown type"
};
🎉 You did it! Pattern matching is now your superpower. Use it to write cleaner, smarter, more expressive code!
