Introduction
Pattern Matching in C# lets you check a value, its type, or its structure and execute logic based on that match in a clean, readable way. Instead of writing long if-else chains or complex type checks, pattern matching lets you express conditions in a more natural and meaningful form.
Pattern matching improves code readability, maintainability, and safety, especially when working with objects, inheritance, and decision-based logic. Over time, C# has evolved pattern matching significantly, making it a powerful language feature in modern applications.
![]()
Real-Life Analogy of Pattern Matching
Imagine a security guard at a building entrance.
If the person is an employee, allow entry
If the person is a visitor, issue a pass
If the person is a delivery person, guide them to the reception
Otherwise, deny access
The guard makes decisions by matching the type and properties of the person.
This is exactly what pattern matching does in C# — it inspects the value and decides the action based on patterns.
Why Pattern Matching Is Needed
Before pattern matching, developers relied heavily on:
These approaches often led to:
Pattern matching simplifies this by combining checks and actions into expressive statements that are easier to understand and safer to execute.
Type Pattern Matching (is Keyword)
Type patterns allow you to check the type of an object and assign it to a variable in a single step.
Without Pattern Matching
if (obj is Employee)
{
Employee emp = (Employee)obj;
Console.WriteLine(emp.Name);
}
With Pattern Matching
if (obj is Employee emp)
{
Console.WriteLine(emp.Name);
}
Explanation
The object is checked for the Employee type and automatically assigned to emp. This avoids explicit casting and improves safety.
Real-Life Example: User Roles
public class Admin
{
public string Name { get; set; }
}
public class Customer
{
public string Name { get; set; }
}
public static void IdentifyUser(object user)
{
if (user is Admin admin)
{
Console.WriteLine($"Admin logged in: {admin.Name}");
}
else if (user is Customer customer)
{
Console.WriteLine($"Customer logged in: {customer.Name}");
}
else
{
Console.WriteLine("Unknown user");
}
}
This approach is commonly used in role-based systems.
Switch Pattern Matching
C# enhanced the switch statement to support patterns instead of simple constant comparisons.
Example: Order Status Handling
public static string GetOrderMessage(object status)
{
return status switch
{
int code when code == 1 => "Order placed",
int code when code == 2 => "Order shipped",
int code when code == 3 => "Order delivered",
_ => "Invalid status"
};
}
Explanation
The switch expression checks the type, applies conditions, and returns a value cleanly without verbose logic.
Property Pattern Matching
Property patterns allow matching based on object properties rather than just types.
Real-Life Example: Employee Validation
public class Employee
{
public string Role { get; set; }
public int Experience { get; set; }
}
public static string CheckEligibility(Employee emp)
{
return emp switch
{
{ Role: "Developer", Experience: >= 3 } => "Eligible for Senior Role",
{ Role: "Developer", Experience: < 3 } => "Junior Developer",
{ Role: "Manager" } => "Management Track",
_ => "Not eligible"
};
}
This makes decision logic extremely readable and expressive.
Null Pattern Matching
Pattern matching provides elegant ways to handle null values.
if (employee is null)
{
Console.WriteLine("Employee data not available");
}
Or within switch expressions:
return employee switch
{
null => "No data",
_ => "Employee exists"
};
This reduces the risk of NullReferenceException.
Relational Patterns
Relational patterns allow matching values using comparison operators.
Example: Grade System
public static string GetGrade(int marks)
{
return marks switch
{
>= 90 => "A",
>= 75 => "B",
>= 60 => "C",
< 60 => "Fail"
};
}
This replaces long if-else chains with a clean structure.
Logical Patterns (and, or, not)
Logical patterns allow combining multiple conditions.
Example: Discount Eligibility
public static string GetDiscount(int age, bool isMember)
{
return (age, isMember) switch
{
(>= 60, _) => "Senior Discount",
(_, true) => "Member Discount",
_ => "No Discount"
};
}
This approach is powerful for complex business rules.
Positional Pattern Matching
Used mainly with records and tuples, positional patterns match values based on position.
public record Point(int X, int Y);
public static string CheckPoint(Point point)
{
return point switch
{
(0, 0) => "Origin",
(_, 0) => "On X-axis",
(0, _) => "On Y-axis",
_ => "Somewhere else"
};
}
Pattern Matching in Real Applications
Pattern matching is widely used in:
It helps express intent clearly and reduces bugs caused by incorrect type handling.
Advantages of Pattern Matching
Pattern matching improves readability, reduces boilerplate code, avoids unsafe casting, and makes decision logic expressive. It also aligns well with modern C# design philosophies.
Pattern Matching vs Traditional If-Else
Traditional if-else logic is verbose and scattered. Pattern matching centralizes decision logic into readable expressions that are easier to maintain and reason about.
Pattern matching in C# is a powerful feature that allows developers to write expressive, safe, and maintainable decision-making logic. From type checks to property validation and relational comparisons, pattern matching replaces complex conditional structures with clear intent-driven code.
Mastering pattern matching is essential for writing modern C# applications and understanding how contemporary frameworks structure their logic.