Improved pattern matching in C# 9

Pattern matching has been around since C# 6. In the previous release of C# (8), several improvements (Microsoft) were made on pattern matching: matching on types/properties, conditional boolean expressions, and allowed to nest multiple switch statements. In the upcoming release of C# 9, pattern matching got enhanced (Microsoft). In this post, some of the new features of the improved pattern matching are highlighted.

Relational patterns

Rather than writing extensive if/else statements, relational patterns can be implemented to improve readability and maintainability.

private static SomeEnum GetPriceDescription (Car c) => c.Price switch
{
    1000 => SomeEnum.Cheap,
    < 5000 => SomeEnum.RatherCheap,
    > 20000 => SomeEnum.Expensive,
    _ => SomeEnum.AllOthers
};

Logical patterns

On the other hand, a relational pattern can be enhanced with logical patterns, to further improve pattern matching.

private static SomeEnum GetPriceDescription (Car c) => c.Price switch
{
    > 0 and <= 1000 => SomeEnum.Cheap,
    > 1000 and <= 5000 => SomeEnum.RatherCheap,
    > 5000 => SomeEnum.Expensive,
    _ => SomeEnum.AllOthers
};
Concrete example

If you ever need to implement a Fibonacci algorithm, now it is time to implement this using a logical pattern.

In C#8, this could be done using:

int fib(int i) {
    if (i <= 2) return 1;
    return fib(i - 2) + fib(i - 1);
};

However, in C#9, this can be done using a logical pattern, such as:

int fib(int i) => i switch {
    int when i <= 2 => 1,
    _ => fib(i - 2) + fib(i - 1)
};

Non logical patterns

Rather than using typeof(T) a non logical pattern can be used for likewise functionality.

private static string GetCarCategory (Car c) 
{
    if (c is not FourWheelDriveCar) 
        return "2WD";
    
    return "4WD";
}
Improved predicates

The previous method pattern can also be applied to more complicated predicates.

DateTime dt = DateTime.Now;
if (dt is { Year: 1999 } and { DayOfWeek: DayOfWeek.Monday }) {}

Which can also be written as:

DateTime dt = DateTime.Now;
if (dt is { Year: 1999, DayOfWeek: DayOfWeek.Monday }) {}

I think this improves readability and allows for easier refinements and refactor posibilities.

Contradictionary readability

Using these new features does not always improve readability.

string x = someRandomString();
if (x is { Length: <5 }) {}

Is something that, in my opinion, is hard to read. I'd prefer:

string x = someRandomString();
if (x.Length < 5) {}

However, I see added value for this when more parameters need to be checked.

Improved null check

This is syntactic sugar, because both statements are exactly the same in IL.

// This can be written
if (e is not null) {}
// Rather than
if (e != null) {}