-
Type Checking and Conversion: When you need to check whether an object is of a particular type and want to convert it to that type at the same time, pattern matching provides a cleaner way to do this, avoiding the need for additional null checking after using the traditional as and is operators.
-
Complex Conditional Logic: When dealing with complex conditional logic, especially when multiple conditions and types are involved, using pattern matching can make the code clearer and more readable. With pattern matching, complex if-else chains or switch statements can be simplified, making the logic more intuitive.
-
Deconstructing Composite Types: When you need to extract values from a composite type (e.g., tuple, custom class, etc.), pattern matching allows you to do the deconstruction directly in the conditional checking, which avoids the need to write additional deconstructing code and makes for cleaner code.
-
Range checking: For scenarios that require range checking, such as checking whether a number falls within a certain interval, the use of the relational pattern introduced in C# 9.0 can greatly simplify the code and make the range checking logic clear at a glance.
-
Logical combination: In the case of logical combination of multiple conditions, such as the need to check whether a value meets one or all of the conditions, the use of logical patterns can be directly in the pattern matching expression using and, or and not operators, avoiding the complexity of logical nesting.
-
Data Validation: Pattern matching can be used in data validation scenarios, especially when the validation logic involves type checking, value range checking, or specific attribute value checking. With pattern matching, all these checks can be done in a single expression, making the validation logic more compact and easier to maintain.
-
Polymorphic Behavior: Pattern matching provides a more flexible alternative to traditional virtual methods or interface implementations when dealing with polymorphic behavior that requires different operations to be performed depending on the object type. This makes it easier to extend or modify behavior without modifying the original class hierarchy.
-
Alternative to the Visitor Pattern: Pattern matching can be used as a cleaner alternative when implementing the Visitor design pattern, especially when dealing with complex object structures. With pattern matching, it is straightforward to handle all types in one place, without the need to create separate visitor methods for each type.
These uses of pattern matching demonstrate its power in simplifying code, improving readability, and flexibly handling different types and conditions. As the C# language evolves, the functionality and application scenarios of pattern matching will be further expanded and deepened.
Let's look at some classic pattern matching coding styles.
is
Assertion The variable str has been safely converted to the string type
object obj = "Hello, World!";
if (obj is string str) {
(str);
}
is
Assertions on nullable types
public record Person(int Id, string? Name, bool? IsActived);
var person = new Person(1, "vipwan", null);
if (person?.IsActived is true)
{
($"Id {} activated");
}
switch
Allows the use of multiple modes, including type mode, constant mode, and var mode, without the need for us to do conversions in advance to save coding effort.
switch (obj) {
case 0:
("Zero");
break;
case var value:
($"Value: {value}");
break;
}
switch
The use of discards in_
in place of a variable
public static string CronEveryNHours(this int n) => n switch
{
(>= 1 and < 24) => $"0 0/{n} * * *",
_ => throw new ArgumentException("n must be between 1 and 24", nameof(n))
};
C# 8.0 introduced attribute patterns, allowing pattern matching based on an object's attributes
public record Person(string Name,int Age);
var person = new Person("vipwan", 30);
//easy understand:in the event thatpersonlet sb. do sth.null,both (... and...)name==vipwan 并both (... and...)age>=18when
if (person is { Name: "vipwan", Age: >= 18 }) {
("vipwan is an adult.");
}
C# 9.0 introduced the logical mode, which allows the use of the logical operatorsand
、or
cap (a poem)not
to combine patterns.
if (number is > 0 and < 10 or 100) {
("Number is between 0 and 10 or equals 100.");
}
tuple model
Allows you to pattern match elements of a tuple, which is useful when dealing with tuple return values or multi-value cases
var numbers = (1, "one", 18);
if (numbers is (1, string name, int age)) {
($"The name of 1 is {name}, age {age}!");
}
list mode
Allows pattern matching on collections such as arrays, lists, etc., which can match the length, elements, and other attributes of the collection. This provides great convenience for pattern matching when dealing with collection data.
int[] numbers = { 1, 2, 3 };
if (numbers is [1, 2, 3]) {
("The array contains the numbers 1, 2, and 3 in that order.");
}
Slicing Mode
Allows you to match a part of a collection, rather than the whole collection. This is especially useful when you only care about a specific part of the collection.
int[] numbers = { 0, 1, 2, 3, 4 };
if (numbers is [0, .., 4]) {
("The array starts with 0 and ends with 4.");
}
Here are just some of the good common pattern matching, as the C# language is enhanced from generation to generation, there may be more new features and improvements are introduced.