Skip to content

Pattern matching improvements C# 9.0code reductionreadability

Enhanced pattern matching with relational, logical, and simplified type patterns.

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, and not 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

More information