C# > Core C# > Control Flow > Pattern Matching with switch
Pattern Matching with `switch` in C#
This snippet demonstrates how to use pattern matching within a `switch` statement in C# to handle different types and values efficiently.
Basic Example: Type and Property Pattern Matching
This example defines a base class `Shape` and two derived classes `Circle` and `Rectangle`. The `DescribeShape` method uses a `switch` statement with pattern matching to determine the type of the shape and its properties (e.g., `Radius` for `Circle`, `Width` and `Height` for `Rectangle`). It also uses a `when` clause to add additional conditions to the pattern match, for instance, identifying 'Large Circle' based on the radius.
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 PatternMatchingExample
{
public static string DescribeShape(Shape shape)
{
switch (shape)
{
case Circle c when c.Radius > 5:
return $"Large Circle with radius {c.Radius}";
case Circle c:
return $"Circle with radius {c.Radius}";
case Rectangle r when r.Width == r.Height:
return $"Square with side {r.Width}";
case Rectangle r:
return $"Rectangle with width {r.Width} and height {r.Height}";
case null:
return "Shape is null";
default:
return "Unknown shape";
}
}
public static void Main(string[] args)
{
Circle circle1 = new Circle { Radius = 3 };
Circle circle2 = new Circle { Radius = 7 };
Rectangle rectangle1 = new Rectangle { Width = 5, Height = 5 };
Rectangle rectangle2 = new Rectangle { Width = 4, Height = 6 };
Shape shape = null;
Console.WriteLine(DescribeShape(circle1));
Console.WriteLine(DescribeShape(circle2));
Console.WriteLine(DescribeShape(rectangle1));
Console.WriteLine(DescribeShape(rectangle2));
Console.WriteLine(DescribeShape(shape));
}
}
Concepts Behind the Snippet
Pattern matching allows you to check if an object matches a specific type and, optionally, extract values from its properties. The `switch` statement evaluates the provided expression against a series of `case` patterns. When a pattern matches, the corresponding code block is executed. The `when` keyword adds a conditional filter to a pattern, allowing for more specific matching logic.
Real-Life Use Case
Consider a scenario where you're processing data from multiple sources, each having a different data structure. Pattern matching allows you to handle these varying structures in a clean and readable manner. For example, processing different message types received from a queue or API endpoint.
Best Practices
Interview Tip
Be prepared to discuss the benefits of pattern matching over traditional `if-else` statements for type checking and data extraction. Explain how pattern matching can lead to more concise and readable code.
When to Use Them
Use pattern matching when you need to handle different types of objects in a polymorphic way, extract data from objects based on their type or properties, and write more readable and maintainable code compared to nested `if-else` statements.
Alternatives
Alternatives to pattern matching include using a series of `if-else` statements with type checking (`is` operator) and property access. However, this can quickly become verbose and difficult to maintain, especially with a large number of types or conditions.
Pros
Cons
Example: Tuple Pattern Matching
This example shows how pattern matching can be used with tuples. The `GetDirection` method takes x and y coordinates and returns a direction string based on their values. We use relational patterns (`>`, `<`) directly within the `switch` statement to check if values are positive or negative. The underscore `_` is used as a discard pattern, matching any value.
public static string GetDirection(int x, int y)
{
return (x, y) switch
{
(0, 0) => "Origin",
(> 0, 0) => "East",
(< 0, 0) => "West",
(0, > 0) => "North",
(0, < 0) => "South",
(_, _) => "Unknown"
};
}
public static void Main(string[] args)
{
Console.WriteLine(GetDirection(1, 0)); // Output: East
Console.WriteLine(GetDirection(0, -1)); // Output: South
Console.WriteLine(GetDirection(0, 0)); // Output: Origin
Console.WriteLine(GetDirection(2, 3)); // Output: Unknown
}
FAQ
-
What happens if no case matches in the switch statement?
If no `case` matches and there is no `default` case, an exception will not be thrown. The control flow simply exits the `switch` statement. However, best practice dictates including a `default` case to handle unexpected inputs gracefully. -
Can I use pattern matching with custom classes?
Yes, you can use pattern matching with custom classes. The example above demonstrates this using the `Shape`, `Circle`, and `Rectangle` classes. -
What is the purpose of the `when` keyword in pattern matching?
The `when` keyword allows you to add a conditional filter to a pattern. The case will only match if the pattern matches and the `when` condition evaluates to true. -
Can I use pattern matching in other contexts besides switch statements?
Yes, pattern matching can also be used in `is` expressions, allowing you to check the type of an object and extract its properties in a single step.