TGViewer
.NET Разработчик .NET Разработчик @netdeveloperdiary · 6.74K subscribers
Post #3247 1.5K
День 2711. #ЗаметкиНаПолях
Разбираем Сопоставления по Шаблону в C#. Часть 5/5

Части 1, 2, 3, 4

Шаблоны списка и среза
Проверяют последовательность поэлементно. [1, 2, 3] соответствует массиву длиной 3 с точно такими же значениями.

Два вспомогательных элемента выполняют основную работу:
- Шаблон среза .. соответствует нулю или более элементам.
- Шаблон отбрасывания _ соответствует одному элементу.

Например:
- [_, >0, ..] аналогично array.Length >= 2 && array[1] > 0.
- [.., <=0, _] аналогично array.Length >= 2 && array[^2] <= 0.

Шаблон списка может содержать не более одного среза. Использование двух срезов для поиска элемента в любом месте массива не допускается.

Типичный пример использования — алгоритмы с малым количеством элементов, где требуются именованные локальные переменные для первых нескольких элементов:
double Sum(ReadOnlySpan<double> vector) {
switch (vector) {
case []:
return 0;

case [double x]:
return x;

case [double x, double y]:
return x + y;

default:
double sum = 0;
for (int i = 0; i < vector.Length; ++i) {
double v = vector[i];
sum += v;
}
return sum;
};
}

Входными данными для шаблона списка может быть любой элемент, который одновременно является счётным (имеет свойство Length или Count) и индексируемым (поддерживает индексатор и форму ^ - счёт от конца - из операторов индекса и диапазона). Это включает массивы, List<T>, Span<T>, ReadOnlySpan<T>, строки и любой пользовательский тип, который предоставляет необходимые элементы.

Шаблоны списков рекурсивные. Элементы шаблона сами могут быть шаблонами:
bool EndsWithListOfOne(List<List<int>> list) =>
list is [.., [_]];

Рекурсивная природа делает шаблоны списков эффективными для анализа полуструктурированных данных, таких как строки CSV-файлов, где некоторые строки содержат больше столбцов, чем другие. Вы сопоставляете данные по форме строки, а не вручную записываете арифметические операции с индексами.

Ловушка выделения памяти
Шаблон var может захватывать результат среза, как в [.. var nMinusOneFirstElems, 37]. Не делайте этого в горячих путях. Компилятор под капотом аллоцирует массив для материализации среза. То же касается строк. Если вы обрабатываете миллионы элементов, выделение памяти на каждое совпадение приведёт к сбою выполнения. Либо откажитесь от захвата среза, либо переключитесь на Span<T>, чтобы компилятор мог избежать выделения памяти.

Известные ограничения
1. Внутри шаблона могут появляться только константы времени компиляции. Значения из конфигурации, базы данных и вычисления времени выполнения не могут. Это главная причина, по которой сопоставление с образцом не используется чаще в бизнес-коде.
2. Предложение when ограничено оператором switch. Вы не можете прикрепить when к оператору if.
3. Шаблон среза может появляться не более одного раза в шаблоне списка.
4. Шаблоны списков с захваченными срезами могут вызывать скрытое выделение памяти.
5. Проверка исчерпываемости не всегда работает. Выражение switch над замкнутой иерархией классов не является доказуемо исчерпывающим для компилятора так, как, например, дискриминированное объединение в F#.

Когда не использовать
Пример ниже с вычислением площади в иерархии классов фигур правильный синтаксически, но демонстрирет антипаттерн:
public static double Area(this Shape shape) =>
shape switch {
Circle c => c.Radius * c.Radius * Math.PI,
Rectangle r => r.Width * r.Height
};

Добавление каждой новой фигуры заставляет искать каждый такой оператор switch в коде. Это классическое нарушение принципа открытости/закрытости. Хуже того, таблица виртуальных методов работает быстрее, чем многократные проверки совместимости типов во время выполнения. Абстрактное свойство Area в базовом классе Shape выигрывает как в плане удобства сопровождения, так и в производительности.

Сопоставление с образцом оправдано, когда:
- Типы, которые вы используете для оператора switch, не связаны наследованием (см. пример с Count в шаблоне типа).
- Вы декодируете структурированные данные, например, парсер или конечный автомат.
- Вы пишете конечный фрагмент условной логики над примитивами, где полиморфизм был бы излишним.

Если вы обнаружите, что добавляете третий тип в оператор switch в объекте предметной области, остановитесь и спросите себя, не является ли правильным решением виртуальный метод.

Источник:
https://blog.ndepend.com/c-pattern-matching-explained/
  • 👍 1
More from @netdeveloperdiary
  1. Sep 26, 2026День 2796. #ЗаметкиНаПолях #AI Рабочий процесс с Copilot для .NET. Продолжение Начало Три…
  2. Sep 25, 2026День 2795. #ЗаметкиНаПолях #AI Рабочий процесс с Copilot для .NET. Начало Проблема с позиц…
  3. Sep 24, 2026День 2794. #Оффтоп #Здоровье Сегодня будет необычный пост. Завтра в Москве стартует конфер…
  4. Sep 23, 2026День 2793. #ЗаметкиНаПолях #SQL 10 Редких Возможностей SQL, Которые Стоит Знать Каждому. Ч…
  5. Sep 22, 2026День 2792. #ЗаметкиНаПолях #SQL 10 Редких Возможностей SQL, Которые Стоит Знать Каждому. Ч…
  6. Sep 21, 2026🔍Тестовое собеседование с Senior C# разработчиком уже завтра 22 сентября(уже завтра!) в 1…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →