Recursive Pattern Matching – A Look at New Language Features in C# 8

ReSharper and Rider support for C# 8Time for another post in our C# 8 series! In this post, we will continue our journey through C# 8 language features, and dive into recursive pattern matching.

In this series, we are looking at:

Folks who have worked with functional languages will probably be familiar with the concept behind the recursive pattern matching C# 8 language feature. This technique can be used to validate and inspect objects more easily based on their shape.

Fundamental for patterns in C# is the is operator, which was extended only recently in C# 7 to also receive patterns additionally to type names for checking objects. Some patterns that we already know are type, constant, and var patterns. With C# 8, discard pattern, positional patterns and property patterns are being introduced.

Discard Pattern

Symbolized with _, the discard pattern matches just any expression. In switch expressions it can also be used as replacement for the default case:

By the way, ReSharper and Rider will now also suggest to remove discard designations (C# 7) when they’re unused:
Remove discard

Deconstruction Pattern

The deconstruction pattern, or sometimes referred to as positional pattern, is used to check for null and to invoke a corresponding Deconstruct method to perform a positional deconstruction. Suppose we have a data type Point that can be deconstructed into its x and y coordinates:

We could then use positional patterns to either just capture the coordinates into a variable, or even to check them against other patterns, like constants:

The deconstruction pattern also works with value tuples, so we could theoretically get rid of the Point type, and declare our method just like string GetDisplayName((int, int) p).

Of course, ReSharper and Rider will help us to get rid of common mistakes with various quick-fixes, for instance by removing type checks, fix component names, or remove component names altogether:
Remove type checks and fix component names

Object Pattern

One drawback of positional patterns, is that the order of deconstruction might not always be obvious. For instance, does a Person deconstruct into firstName and lastName, or lastName and firstName? A more flexible solution is described by the object pattern also known as property pattern, which works – as the name suggests – on the structure of an object. For further explanation, we can consider a more complex object structure:

And again, we can check for conditions and capture data:

As we can see from the comparison, property patterns are very flexible in their application. The { } pattern is basically checking, whether a variable or member is not null. We can further check their members, and their sub-members by simply continuing with the property pattern. The example from above also illustrates how different patterns can be used in combination. Here is another one, given we can deconstruct a Person into its lastName, firstName, and address:

And as you may have guessed, ReSharper and Rider also add some sugar when using property patterns, like code completion and quick-fixes:
Complete and fix member names

Download ReSharper Ultimate 2019.2 EAP or check out Rider 2019.2 EAP to start taking advantage of ReSharper’s language support updates and C# 8. We’d love to hear your thoughts!

About Matthias Koch

Matthias is a passionate C# developer and likes to talk about clean code, testing and tooling in general. Much of his spare time in the last years was devoted to his very own open source projects, including NUKE. He is working at JetBrains as developer advocate for the .NET department. Follow him on Twitter.
This entry was posted in How-To's and tagged , , , , , , . Bookmark the permalink.

2 Responses to Recursive Pattern Matching – A Look at New Language Features in C# 8

  1. Sam says:

    @JetBrains, great work as usual.

    if (c.LastName != null && c.Address.City != null && c.Address?.ZipCode == 99999)
    Console.WriteLine($”Hi {c.LastName} from {c.Address.City}”);

    // With C# 8
    if (c is { LastName: { } lastName, Address: { City: { } city, ZipCode: 99999 } })
    Console.WriteLine($”Hi {lastName} from {city}”);

    Yuck. I know the C# LDT and some subset of users is excited about pattern matching, but I really can’t understand why anyone would think the second is more readable than the first. Especially having null checks that don’t actually contain the word “null” anywhere.

    I hope you guys provide a refactoring to turn pattern matching syntax back into “normal” syntax. :)

    • Matthias Koch says:

      It’s a very new C# indeed! One fact that we cannot deny though, is that the second example is way more DRY. No repetition at all :)

Leave a Reply

Your email address will not be published. Required fields are marked *