[C#] Pattern Matching explained with code
April 18, 2026•781 words
Pattern matching is a powerful feature in C# that allows you to match an expression against a pattern and conditionally execute code based on the match. It provides a concise and expressive way to handle various scenarios, such as type checking, constant comparison, and data deconstruction. C# supports several types of patterns, each serving a specific purpose.
1\. Declaration Pattern
The declaration pattern checks the runtime type of an expression and, if a match succeeds, assigns the expression result to a declared variable. It is typically used in conjunction with the is operator or the switch expression.
// Example: Checking if an object is a string and assigning its value to a variable
if (obj is string s){
Console.WriteLine($"The object is a string: {s}");
} else {
Console.WriteLine("The object is not a string.");
}
2\. Type Pattern
The type pattern checks the runtime type of an expression. It can be used with the is operator or the switch expression.
// Example: Checking the type of an object
object obj = "Hello";
if (obj is string){
Console.WriteLine("The object is a string.");
} else {
Console.WriteLine("The object is not a string.");
}
3\. Constant Pattern
The constant pattern tests if an expression result equals a specified constant. It is commonly used with the switch expression.
// Example: Checking a value against constant values
int value = 2;
switch (value){
case 1:
Console.WriteLine("Value is 1");
break;
case 2:
Console.WriteLine("Value is 2");
break;
default:
Console.WriteLine("Value is something else");
break;
}
4\. Relational Patterns
Relational patterns compare an expression result with a specified constant using relational operators like <, \>, <=, and \>=. They can be used in the switch expression.
// Example: Checking a value against relational patterns
int value = 10;
switch (value){
case < 0:
Console.WriteLine("Value is negative");
break;
case > 10:
Console.WriteLine("Value is greater than 10");
break;
case >= 0 and <= 10:
Console.WriteLine("Value is between 0 and 10 (inclusive)");
break;
default:
Console.WriteLine("Value is something else");
break;
}
5\. Logical Patterns
Logical patterns test if an expression matches a logical combination of patterns. They can be combined using the and, or, and not operators.
// Example: Checking a value against a logical combination of patterns
object obj = "hello";
if (obj is string || obj is int) {
Console.WriteLine("It's either a string or an integer.");
}
6\. Property Pattern
The property pattern tests if an expression's properties or fields match nested patterns. It is commonly used for object deconstruction.
// Example: Checking properties of an object
Person person = new Person("John", 30);
if (person is { Name: "John", Age: 30 }){
Console.WriteLine("Person is John, aged 30");
}
else{
Console.WriteLine("Person does not match the pattern");
}
7\. Positional Pattern
The positional pattern deconstructs an expression result and tests if the resulting values match nested patterns. It is often used with tuples or objects that implement the deconstruction pattern.
// Example: Deconstructing an istance of Point
public class Point {
public int X { get; }
public int Y { get; }
public Point(int x, int y) { X = x; Y = y; }
public void Deconstruct(out int x, out int y) {
x = X; y = Y;
}
}
Point point = new Point(3, 4);
if (point is (3, 4)) {
Console.WriteLine("Point is at (3, 4)");
}
8\. var Pattern
The var pattern matches any expression and assigns its result to a declared variable. It is useful when you don't need to check the type of the expression.
// Example: Assigning an expression result to a variable
object obj = "Hello";
if (obj is var message){
Console.WriteLine($"The object is: {message}");
}
9\. Discard Pattern
The discard pattern matches any expression without assigning its result to a variable. It is denoted by an underscore (___).
// Example: Ignoring part of a deconstructed result
(int x, _) = (3, 4);
Console.WriteLine($"The value of x is: {x}");
10\. List Patterns (C# 11)
List patterns, introduced in C# 11, test if sequence elements match corresponding nested patterns. They can be used with any type that implements IEnumerable.
// Example: Checking elements of a list
List numbers = new List { 1, 2, 3, 4, 5 };
if (numbers is [1, 2, _, 4, 5]){
Console.WriteLine("The list matches the pattern");
}else{
Console.WriteLine("The list does not match the pattern");
}
Pattern matching in C# provides a concise and expressive way to handle various scenarios, such as type checking, constant comparison, and data deconstruction. By understanding and utilizing these patterns, developers can write more readable and maintainable code.