Pattern matching is a technique where you test an expression to determine if it has certain characteristics.
C# pattern matching provides more concise syntax for testing expressions and taking action when an expression matches.
The "is" supports pattern matching to test an expression and conditionally declare a new variable to the result of that expression.
The "switch" enables you to perform actions based on the first matching pattern for an expression.
These two expressions support a rich vocabulary of patterns.
1. Type Checking and Casting
Use is to check an object's type and cast it safely in a single step.
//before
if (obj is string)
{
string s = (string)obj;
Console.WriteLine($"length {s.Length}");
}
//after
if (obj is string s)
{
Console.WriteLine($"length {s.Length}");
}
2. Relational Patterns
Use comparison operators (<, >, >=, <=) directly in pattern matching.
//before
if (age > 0 && age < 18)
{
Console.WriteLine("Minor");
}
//after
if (age is > 0 and < 18)
{
Console.WriteLine("Minor");
}
3. Logical Patterns
Combine patterns using logical operators: and , or , not
//before
if (number != 0)
{
Console.WriteLine("Non-zero");
}
//after
if (number is not 0)
{
Console.WriteLine("Non-zero");
}
4. Positional Patterns
Match values using tuple-like syntax or deconstruction of objects.
public class Point
{
public int X { get; }
public int Y { get; }
public void Deconstruct(out int x, out int y) => (x, y) = (X, Y);
}
//before
if (point.X == 0 && point.Y == 0)
{
Console.WriteLine("Origin");
}
//after
if (point is (0, 0))
{
Console.WriteLine("Origin");
}
5. Property Pattern
Match based on the values of specific properties in an object.
public class Person
{
public int Age { get; set; }
public string Name { get; set; }
public Role Role { get; set; }
}
//before
if (person.Age > 18 && person.Name == "John")
{
Console.WriteLine("Adult named John");
}
//after
if (person is { Age: > 18, Name: "John" })
{
Console.WriteLine("Adult named John");
}
| and |
{ Age: > 18, Name: "John" } |
| or |
NOT supported within a single property pattern |
| not |
{ Age: not 0 } |
(more example)
//before
if (obj is Person)
{
Person p = (Person)obj;
Console.Write($"{p.Name}, your role is {p.Role}.");
}
//after
if (obj is Person p)
{
Console.Write($"{p.Name}, your role is {p.Role}.");
}
//before
if (person !=null && person.Age >= 18 && person.Role == Role.User)
{
Console.WriteLine("Adult user detected.");
}
//after
if (person is { Age: >= 18, Role: Role.User })
{
Console.WriteLine("Adult user detected.");
}
//before
if (person !=null && person.Role!= Role.Admin )
{
Console.WriteLine("Access denied. Admins only.");
}
//after
if (person is not { Role: Role.Admin })
{
Console.WriteLine("Access denied. Admins only.");
}
//before
if (person !=null && (person.Name== "Alice“ || person.age<18)
{
Console.WriteLine("Access denied. Admins only.");
}
//after
if (person is { Name: "Alice" } or { Age: < 18 })
{
Console.WriteLine("Special case or minor.");
}
6. Switch Expressions
A concise way to match patterns and return values using switch
//before
string result;
if (obj is int i)
result = $"It's an int: {i}";
else if (obj is string s)
result = $"It's a string: {s}";
else if (obj == null)
result = "It's null";
else
result = "Unknown";
//after
string result = obj switch
{
int i => $"It's an int: {i}",
string s => $"It's a string: {s}",
null => "It's null",
_ => "Unknown"
};
//Example 2
string result = person switch
{
{ Role: Role.Admin } => "Welcome, Admin!",
{ Age: < 18 } or { Age:<4} => “Just older than 18 or younger than 4",
{ Name: "Alice" } => "Hi Alice!",
{ Name: var name } when name.StartsWith("A")=>"Name starts with A."
};
7. List Pattern Matching
Match elements in arrays or lists by position, length, and values.
int[] numbers = { 1, 2, 3 };
//before
if (numbers.Length == 3 && numbers[0] == 1 &&
numbers[1] == 2 &&
numbers[2] == 3)
{
Console.WriteLine("Exact match!");
}
//after
if (numbers is [1, 2, 3])
{
Console.WriteLine("Exact match!");
}
//Example 2
int[] scores = { 100, 90, 80 };
if (scores is [100, ..])
{
Console.WriteLine("Starts with a perfect score!");
}
//Example 3 (more complex)
List<Person> people = GetPeople();
if (people is [
{ Age: > 20 }, //First item
_, _, _, { Age: >= 30 }, //5th item must be Age >= 30
.., //Skip to the rest
{ Name: “Vahid”} //Last item
] and
{ Count: > 20 }) //List must have more than 20 elements
{
Console.WriteLine("Matched pattern!");
}