C# > Language Features by Version > C# 6 to C# 12 Highlights > Pattern Matching Enhancements (C# 8+)
Type and Property Pattern Matching with Enhanced 'when' Clauses
This example demonstrates enhanced pattern matching capabilities introduced in C# 8 and improved in later versions. It showcases type patterns, property patterns, and the use of 'when' clauses for more complex conditional logic within a single `switch` statement. We will also show how to use the `is` operator to check properties within an object, which is useful for cases where an object may be null or have properties that need to be validated before use.
Basic Example with Type and Property Patterns
This code defines a `Shape` class and two derived classes, `Circle` and `Rectangle`. The `DescribeShape` method uses a `switch` expression with pattern matching to determine the type of the shape and extract relevant properties like `Radius`, `Width`, and `Height`. The `when` clauses add further conditions to refine the matching logic. Notice how the `null` check is handled directly within the pattern matching construct. Also, note that `var r`, `var w`, and `var h` are used to capture the values of the matched properties directly.
using System;
public class Shape
{
public double Area { get; set; }
}
public class Circle : Shape
{
public double Radius { get; set; }
}
public class Rectangle : Shape
{
public double Width { get; set; }
public double Height { get; set; }
}
public static class PatternMatchingDemo
{
public static string DescribeShape(Shape shape)
{
return shape switch
{
Circle { Radius: var r } when r > 0 => $"Circle with radius {r}",
Rectangle { Width: var w, Height: var h } when w > 0 && h > 0 => $"Rectangle with width {w} and height {h}",
Shape s when s.Area > 100 => "Large shape with area > 100",
null => "No shape",
_ => "Unknown shape"
};
}
public static void Main(string[] args)
{
Circle circle = new Circle { Radius = 5, Area = 78.5 };
Rectangle rectangle = new Rectangle { Width = 10, Height = 6, Area = 60 };
Shape largeShape = new Shape { Area = 150 };
Console.WriteLine(DescribeShape(circle));
Console.WriteLine(DescribeShape(rectangle));
Console.WriteLine(DescribeShape(largeShape));
Console.WriteLine(DescribeShape(null));
}
}
Concepts Behind the Snippet
Pattern matching allows you to write more concise and readable code for conditional logic based on the structure and properties of objects. It combines type checking, property access, and conditional branching into a single expression. The `switch` expression provides a declarative way to express complex logic that would otherwise require multiple `if-else` statements. The `when` clause allows for applying further filtering conditions to the pattern match.
Real-Life Use Case
Imagine processing different types of messages from a queue. Each message type has a different structure and requires specific handling. Pattern matching allows you to easily route messages to the appropriate handler based on their type and content. For instance, you might have `OrderMessage`, `PaymentMessage`, and `ShippingMessage` classes, and your processing logic could use pattern matching to dispatch each message to the correct handler method.
Best Practices
Interview Tip
Be prepared to explain the benefits of pattern matching over traditional `if-else` statements. Highlight the improved readability, conciseness, and expressiveness. Also, be ready to discuss the different types of patterns available (type patterns, property patterns, positional patterns, etc.) and when to use each one.
When to Use Pattern Matching
Pattern matching is particularly useful when dealing with algebraic data types, discriminated unions, or any situation where you need to perform different actions based on the structure and properties of objects. It's also a good choice when you want to avoid nested `if-else` statements and create more readable and maintainable code.
Alternatives
The primary alternative to pattern matching is using a series of `if-else` statements with type checks (using the `is` operator) and property accessors. However, this approach can quickly become verbose and difficult to read, especially with complex conditions. Another option involves using the visitor pattern in Object Oriented programming, but that can add complexity.
Pros
Cons
FAQ
-
What versions of C# support pattern matching?
Pattern matching was introduced in C# 7.0 and has been significantly enhanced in subsequent versions, especially C# 8.0 and later. C# 9.0 introduced relational patterns, logical patterns, and more. -
What is the discard pattern?
The discard pattern (`_`) is a wildcard that matches any value. It's typically used in a `switch` statement to handle the default case when none of the other patterns match. -
What are 'when' clauses in pattern matching?
A 'when' clause adds an additional boolean condition to a pattern. The pattern will only match if both the pattern itself and the 'when' clause condition are true.