Pattern Matching Enhancements in C#



Pattern matching is one of the most powerful features in modern C#. It allows us to check the shape or structure of data and then extract or take action directly in a clear and readable way.

With each new version of C#, Microsoft has improved pattern matching to make our code safer, shorter, and easier to understand.

What is Pattern Matching?

Pattern matching in C# involves verifying if a value matches a specific pattern or type, and then using that value without needing extra type casting or conditional statements.

Following are the list of pattern matching −

Type Pattern in C#

Type pattern in C# is basic pattern matching. Before pattern matching, we have to do manual type checking and casting. But after pattern matching, it's easy to verify if a variable is of a specific type and cast it automatically.

Example: Before Pattern Matching

In the following code snippet, we can see that we are manually typechecking and casting −

object obj = "Hello";

if (obj is string) {
   string str = (string)obj;
   Console.WriteLine(str.ToUpper());
}

Example: With Pattern Matching

In the following example, we can do type checking and casting in just one step; see the below code snippet −

object obj = "Hello";

if (obj is string str) {
   Console.WriteLine(str.ToUpper());
}

Explanation − The "is string str" checks if "obj" is a string and automatically assigns it to a variable "str"s.

Constant Pattern in C#

Constant pattern in C# is used to match a value directly against a constant. It an alternative syntax for the "==" operator when the right-hand operand is a constant.

We use a constant pattern to check whether the result of an expression is equal to a specified constant.

Example: If a Number is Constant or Not

In the following code snippet, we use a constant pattern to check if the given number is constant or not.

int number = 10;

if (number is 10) {
   Console.WriteLine("Number is exactly 10");
}

Explanation: This checks if number is equal to 10. It's a simple "==" check written more clearly.

Relational Patterns in C#

A relational pattern can be used in C# (version 9) to compare an expression result with a constant. In a relational pattern, we can use any of the relational operators <, >, <=, or >=. The right-hand part of a relational pattern must be a constant expression.

The constant expression can be of an integer, floating-point, char, or enum type.

Example: Range of Age

In the following code snippet, we use the comparison operator to compare the age to verify whether you are an adult or not.

int age = 25;
if (age is >= 18 and < 60) {
   Console.WriteLine("You are an adult");
}

Explanation: The >= and < patterns check if the age is between 18 and 59.

Logical Patterns (and, or, not) in C#

Logical pattern in C# allows us to combine multiple patterns using logical operators such as and, or, and not.

It helps you write complex conditions in a more readable and natural way.

Example: Combining Multiple Patterns

In the following code snippet, we use the logical operator to combine multiple patterns, such as relational, type, and constant patterns.

int number = 15;

if (number is > 0 and < 100) {
   Console.WriteLine("Number is positive and less than 100");
}

if (number is not 0) {
   Console.WriteLine("Number is not zero");
}

Switch Expression Patterns in C#

Switch expression works perfectly with pattern matching; it helps to make the C# code more concise and readable and also helps to avoid using the long if-else statement.

Example: Switch Expression Pattern

In the following code snippet we demonstrate the working of the switch expression pattern:

string GetDayType(int day) => day switch {
   1 or 7 => "Weekend",
   >= 2 and <= 6 => "Weekday",
   _ => "Invalid day"
};
Console.WriteLine(GetDayType(1));

Explanation

  • 1 or 7 means either 1 or 7 will match the same case.
  • _ is the discard pattern, meaning "anything else".

Property Pattern in C# (C# 8+)

The property pattern in C# allow matching value of properties that defined on an object.

We write the property name, followed by a colon (:), and then the value to match. We can check multiple properties by separating them with commas (,).

Example: Checking the Properties Value

In the following code snippet, we are checking the specified name and the range of age, using the property pattern.

var person = new { Name = "Aman", Age = 25 };

if (person is { Name: "Aman", Age: >= 18 }) {
   Console.WriteLine("Adult named Aman");
}

Explanation: This checks if the Name property is "Aman" and Age is greater than or equal to 18.

Positional Pattern in C#

Positional patterns let you match an object by comparing its values inside parentheses. It is based on deconstruction and can be used with tuples, records, or types that support deconstruction.

The object's values are extracted using deconstruction, and the pattern matches if each value matches.

Example: Positional Pattern Matching

In this code snippet, we are using a positional pattern with a switch expression to check the values inside a tuple called 'point'.

var point = (10, 20);

string result = point switch {
   (0, 0) => "Origin",
   (var x, 0) => $"X-axis at {x}",
   (0, var y) => $"Y-axis at {y}",
   _ => "Somewhere else"
};

Console.WriteLine(result);

Parenthesized Patterns in C#

A parenthesized pattern in C# is used to group multiple patterns together using parentheses ( ). It helps make complex pattern matching conditions clearer and easier to read, especially when combining patterns with and, or, and not.

Example: Parenthesized Patterns Matching

In this code snippet, we are using a parenthesized pattern to check whether the number n falls within certain ranges −

int n = 10;

if (n is (>= 5 and <= 15) or (>= 20 and <= 25)) {
   Console.WriteLine("Number is in a valid range");
}

Tuple Patterns in C#

Tuple pattern matching in C# lets us compare multiple values at once by matching them against a tuple pattern.

This pattern matching is useful when you want to check the relationship between two or more variables together instead of checking each one separately.

Example: Finding Position of a Point on 2D Plane

In this code snippet, we are using tuple pattern matching with a switch expression to find the position of a point on a 2D plane based on its x and y coordinates.

int x = 0, y = 5;

string position = (x, y) switch {
   (0, 0) => "Origin",
   (0, _) => "On Y-axis",
   (_, 0) => "On X-axis",
   _ => "In Quadrant"
};

Console.WriteLine(position);

Explanation

  • The _ (underscore) means "any value".
  • This makes multiple-value matching simple and readable.

List Pattern in C#

List pattern in C# allows us to match the elements of an array or list directly using pattern matching syntax. It helps in checking the structure, content, or length of a list without writing loops or extra conditions.

Example: List Pattern Matching in C#

In this example, we are using List Pattern Matching (C# 11+) to check the structure and contents of an array named numbers.

int[] numbers = { 1, 2, 3 };

if (numbers is [1, 2, 3]){
   Console.WriteLine("Exact match");
}

if (numbers is [1, .., 3]){
   Console.WriteLine("Starts with 1 and ends with 3");
}

Explanation

  • [1, 2, 3] matches the exact list.
  • [1, .., 3] means starts with 1, ends with 3, and has anything in between.

Pattern Matching Enhancement Over C# Version

Version Enhancement Example
C# 7 Type pattern obj is string s
C# 8 Property & positional patterns person is { Age: >18 }
C# 9 Relational & logical patterns x is >0 and <100
C# 11 List patterns arr is [1, .., 3]

Conclusion

Pattern Matching is an important feature in modern C# programming that makes the data checks simpler and clearer, and thereby it helps us write less code with better readability and fewer bugs.

Advertisements