List patterns, introduced in C# 11.0 (.NET 7.0), extend pattern matching to arrays and lists. You can match elements at specific positions, use slice patterns (..) to match variable-length sequences, and combine patterns to express complex list structure checks. This feature transforms verbose index-based logic into clear, declarative patterns.
Why it matters
- Replaces verbose index-based conditionals with readable pattern expressions.
- Enables matching on list structure (first, last, or middle elements) without explicit indexing.
- Integrates seamlessly with switch expressions and pattern matching.
- Makes code intent clearer and less error-prone than manual index arithmetic.
Cautions
- List patterns work with collections implementing
IEnumerable; performance depends on the collection type (arrays are efficient; linked lists require enumeration). - Slice patterns consume remaining elements; ensure your intent matches (do you want to ignore them or process them?).
- A non-matching list pattern does not throw; it simply evaluates as no match. Still include a fallback arm in
switchexpressions for clarity and safety. - Debugging list patterns can be harder than explicit index checks; test thoroughly.
Match array elements by position and value
List patterns allow you to match specific positions and values in an array or list, enabling powerful pattern-based control flow.
Valid since C# 11.0
Without var
int[] numbers = [1, 2, 3];
if (numbers.Length >= 2 && numbers[0] == 1 && numbers[1] == 2)
{
Console.WriteLine("Starts with 1, 2");
}
else if (numbers.Length == 3 && numbers[0] == 1 && numbers[2] == 3)
{
Console.WriteLine("Pattern: 1, any, 3");
}
With var
int[] numbers = [1, 2, 3];
string result = numbers switch
{
[1, 2, ..] => "Starts with 1, 2",
[1, _, 3] => "Pattern: 1, any, 3",
_ => "No match"
};
Console.WriteLine(result);
Slice patterns for variable-length lists
List patterns with slice patterns (`..`) allow matching the first, last, or middle elements while ignoring or capturing the rest.
Valid since C# 11.0
string Summarize(int[] numbers) => numbers switch
{
[var first, .. var rest] => $"First: {first}, Rest count: {rest.Length}",
[var only] => $"Single element: {only}",
[] => "Empty list"
};