.NET Tools
Essential productivity kit for .NET and game developers
List and Span Pattern Matching – Using C# 11 in Rider and ReSharper
The .NET 7 SDK arrived a few months ago, with many .NET developers looking forward to this release and the brand-new C# language features that come along with it. If you haven’t put your fingers on it yet, all you need is:
- Download the latest .NET SDK
- Update your
global.json
if you have one - Update the
TargetFramework
in your project tonet7.0
(only for some features) - Update the
LangVersion
property in your project to11.0
orpreview
In this series, we will dive into the most interesting features that are coming with C# 11 and show how we updated ReSharper and Rider to support you in applying them to your codebase with ease:
- List and Span Pattern Matching
- Raw Strings, UTF-8 Strings, and Multiline Interpolations
- Required Keyword, Checked Operators, nameof Operator Scope
- Static Interface Members, Generic Attributes, Auto-Default Structs
This first post will walk you through list patterns and span pattern matching.
List Patterns
Since the introduction of pattern matching in C# 7, we could see continuous enhancements in C# 8, C# 9, and C# 10. It comes unsurprisingly but much appreciated, that in the .NET 7 release we got another feature – list patterns (design proposal) – that pushes C# into the functional programming direction. List patterns also work on arrays; the compiler really only needs to have a Length
/Count
property and index accessors to do its magic.
Central to list patterns is the []
syntax. In the most simple form []
expresses an empty list or array. Inside the brackets, you can freely use any other pattern to match individual elements. New in the lineup is the range pattern, which matches the sequence of elements (or none) and can either be discarded or captured (var
pattern).
In the following example, we will apply different list patterns to recursively check if a given integer array is sorted or not:
bool IsSorted<T>(Span<T> numbers) where T : INumber<T> { return numbers switch { // Empty list and single item is always sorted [] or [_] => true, // If first and second of the current range are not sorted => false [var first, var second, ..] when first > second => false, // If none of the above conditions are met, proceed with the rest [_, .. var rest] => IsSorted(rest), // Throw if numbers are null null => throw new ArgumentNullException(nameof(numbers)) }; }
This example really only scratches the surface of what’s possible. You can also use relational patterns, property patterns, or even nested list patterns:
bool Examples(int[][] array) { return array switch // Array with single element (another array) { [[< 5]] => true, // ... with single element lower 5 [{ Length: > 8 }] => false, // ... with length greater 8 [[]] => true, // ... with no elements }; }
ReSharper and Rider 2022.3 fully support list patterns in terms of error checking, code completion and code analysis. We are still working on code inspections to suggest the use of list patterns in existing code, as well as additional refactoring actions.
Some recent tweets by our very own Alexander Shvedov should give you an impression of what cool features will be coming:
Spent nearly a week making this work… We are now able to find C# expressions accessing the same values that were pattern-matched before – this helps to push/consolidate more code into the pattern pic.twitter.com/aJC93hOUVw
— Aleksandr Shvedov (@controlflow) January 18, 2023
Span Pattern Matching
The introduction of Span<T>
in C# 7.2 greatly improved our ability to write high-performance and allocationless code in safe C#. With C# 11, the use of Span<char>
values as a replacement for string instances is greatly simplified by allowing you to pattern-match string constants against values of Span<char>
and ReadOnlySpan<char>
:
bool IsSpecial(ReadOnlySpan<char> str) => str switch { "namespace" => true, "type" or "member" => true, _ => false };
ReSharper and Rider support this new language feature and allow you to convert from SequenceEqual
to the leaner form with a quick-fix. For instance, as to the following switch
expression:
Conclusion
List and Span pattern matching are a great opportunity to refactor existing code and make it more concise. Let us know if you’d like to see any particular feature we haven’t thought about yet!
Also, stay tuned for the next blog post where we will take a look at everything related to strings!