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
C#
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
}
}
C#
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
, andnot
keywords - 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
not
pattern 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