C# 8.0's pattern matching was powerful but lacked support for combining patterns with logical operators or comparing values with relational operators. Complex conditions still required verbose if statements or multiple pattern branches.
C# 9.0 significantly enhances pattern matching with relational patterns (<, >, <=, >=), logical patterns (and, or, not), and the ability to use type patterns without declaring variables. These improvements make complex conditional logic more expressive and concise.
Code
csharp
public string ClassifyNumber(int number) => number switch
{
< 0 => "Negative",
0 => "Zero",
> 0 and < 10 => "Single digit",
>= 10 and < 100 => "Double digit",
>= 100 => "Three or more digits"
};
public bool IsLetter(char c) => c is (>= 'a' and <= 'z') or (>= 'A' and <= 'Z');
public decimal CalculateDiscount(Customer customer) => customer switch
{
{ IsVip: true, Orders: > 100 } => 0.20m,
{ IsVip: true } or { Orders: > 50 } => 0.10m,
not null => 0.05m,
_ => 0m
};
public string DescribeTemperature(double temp) => temp switch
{
< 0 => "Freezing",
>= 0 and < 10 => "Cold",
>= 10 and < 20 => "Cool",
>= 20 and < 30 => "Warm",
>= 30 => "Hot"
};
// Simplified type patterns
public bool IsNumericType(object obj) => obj is int or long or double or decimal;
public void Process(object obj)
{
if (obj is not null and not string)
{
// Process non-null, non-string objects
}
}csharp
public string ClassifyNumber(int number)
{
if (number < 0)
return "Negative";
if (number == 0)
return "Zero";
if (number > 0 && number < 10)
return "Single digit";
if (number >= 10 && number < 100)
return "Double digit";
return "Three or more digits";
}
public bool IsLetter(char c)
{
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}
public decimal CalculateDiscount(Customer customer)
{
if (customer == null)
return 0m;
if (customer.IsVip && customer.Orders > 100)
return 0.20m;
if (customer.IsVip || customer.Orders > 50)
return 0.10m;
return 0.05m;
}
public string DescribeTemperature(double temp)
{
if (temp < 0)
return "Freezing";
if (temp >= 0 && temp < 10)
return "Cold";
if (temp >= 10 && temp < 20)
return "Cool";
if (temp >= 20 && temp < 30)
return "Warm";
return "Hot";
}
public bool IsNumericType(object obj)
{
return obj is int || obj is long || obj is double || obj is decimal;
}
public void Process(object obj)
{
if (obj != null && !(obj is string))
{
// Process non-null, non-string objects
}
}Notes
- Relational patterns: Use
<,>,<=,>=to compare against constant values - Logical patterns: Combine patterns with
and,or, andnotkeywords - Simplified type patterns: Test types without declaring a variable when you don't need the value
- Parentheses can group patterns to control precedence:
(pattern1 or pattern2) and pattern3 - The
notpattern is particularly useful for null checks:obj is not null - Logical operators have precedence:
not>and>or - Can combine all these features for highly expressive pattern matching