Підрозділ 9.6
Патерни списків
Пояснює list patterns: повний збіг зі списками й масивами, підстановку _, slice-патерн .., отримання елементів у змінні та поєднання з властивостями колекцій.
9.6. Патерни списків
Патерни списків (list patterns) — найновіший різновид патернів у C#, доданий у версії C# 11 (.NET 7, 2022). Вони дозволяють зіставляти масиви та колекції зі структурними зразками: перевіряти кількість елементів, значення на конкретних позиціях, першого і останнього елементів, а також довільний «зріз» посередині.
Які типи підтримуються
Патерн списків працює з будь-яким типом, що є лічильним (має властивість Length або Count) і індексованим (має індексатор з int або Index). Інтерфейс тут не потрібен — компілятор шукає члени за іменами:
T[]— масиви будь-якого типу;List<T>,IList<T>— списки;Span<T>,ReadOnlySpan<T>— span-и.
Для slice з захопленням (.. var x) потрібно більше: індексатор з Range або метод Slice(int, int). Він є в масивів, string, Span<T> і List<T> (метод Slice додано в .NET 8), але немає в IList<T>: il is [var a, .. var rest] дає помилку CS1503, хоча il is [1, ..] (slice без захоплення) компілюється. Захоплений slice масиву — це новий масив-копія.
IEnumerable<T> без Count/Length не підтримується — компілятор не може ефективно перевіряти довжину ліниво-обчислюваних послідовностей.
Як компілятор перевіряє патерн
Для патерну [a, b, c] компілятор генерує приблизно:
collection.Length == 3 &&
collection[0] matches a &&
collection[1] matches b &&
collection[2] matches cТобто спочатку перевіряється довжина, потім — кожна позиція через відповідний патерн. Це важливо для продуктивності: якщо довжина не збігається — позиції не перевіряються.
Синтаксис: елементи та slice


Елементи у дужках перевіряють позиції. В кожній позиції може стояти будь-який патерн — константа, _, реляційний, var:
readings is [72, 88, 95, 101, 88] // точний збіг усіх 5 елементів
readings is [> 70, _, _, > 90, _] // реляційні в позиціях
readings is [var first, .., var last] // захопити крайніSlice .. — це «довільна кількість елементів» (включно з нулем). Він може стояти лише один раз у патерні:
[72, ..] // починається з 72
[.., 88] // закінчується на 88
[72, .., 88] // починається 72, закінчується 88, між ними будь-що
[_, .., _] // ≥2 елементи
[..] // будь-яка довжина ≥0Сам slice теж можна захопити у змінну: .. var назва (див. приклад нижче).
Клінічний приклад: аналіз серії вимірювань
Захоплення slice у змінну
Частину між крайніми елементами можна захопити у змінну через .. var назва. Це дозволяє далі обробляти «середину» масиву:
Поєднання патерну списків з патерном властивостей
Якщо потрібно перевірити і структуру списку, і властивості колекції, обидва патерни можна поєднати через and:
if (readings is { Length: >= 3 } and [var first, _, _, .. var rest])
{
Console.WriteLine($"Перший: {first}, решта: {string.Join(",", rest)}");
}Тут { Length: >= 3 } — патерн властивостей (перевіряє довжину), and [...] — патерн списків (зіставляє структуру). Разом — єдина умова. Щоправда, в цьому конкретному прикладі перевірка довжини надлишкова: [var first, _, _, .. var rest] сам по собі вимагає щонайменше трьох елементів. Комбінація має сенс, коли властивість обмежує те, чого патерн списку не виражає, — наприклад, верхню межу: { Length: <= 10 } and [> 100, ..] («коротка серія, що почалась із тахікардії»).
Де патерни списків практично корисні
Патерни списків особливо виразні в задачах, де структура масиву сама по собі несе інформацію:
- Парсинг рядків команд:
["add", var name, var value],["remove", var id],[] - Валідація форматів: масив токенів повинен починатись і закінчуватись певними маркерами
- Обробка кадрів протоколу: перший байт — тип, останній — контрольна сума
- Аналіз трендів:
[> threshold, .., < threshold]— значення перетнуло поріг
Для великих або нескінченних послідовностей (IEnumerable<T>) патерни списків не підходять — там слід використовувати LINQ або явні цикли.