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

Часть 1

Оператор is - основа сопоставления по шаблону
Любой шаблон можно проверить с помощью выражения is, и результатом будет логическое значение (bool).

Сопоставление по шаблону — не магия, а всего лишь синтаксический сахар. Компилятор C# преобразует шаблоны в обычный IL-код, похожий на тот, который генерировался бы из обычных выражений. Поэтому сопоставление по шаблону не медленнее, чем эквивалентные булевы выражения, и компилятор иногда будет переписывать шаблон более агрессивно, чем когда переписывает обычную цепочку ||.

Проверка на null
Простейший шаблон проверки на null:
static void NullPattern(object o, int? ni) {
if (o is null) Console.WriteLine("o is null");
if (!(ni is null)) Console.WriteLine(ni.Value);
}

С C# 9 доступна отрицательная форма is not null, и это рекомендуемый способ вместо != null:
if (obj is not null) { … } //C# 9

Шаблон лучше, чем == null, потому что оператор == может быть перегружен, и существуют классы, в которых оператор == возвращает true для экземпляра, сравниваемого с null (например, UnityEngine.Object в Unity). Шаблон сравнения всегда представляет собой сравнение по ссылке.

Сопоставление литеральных значений
Шаблон константы сравнивает значение с константой времени компиляции:
static void ConstantPattern(double d, string str) {
if (d is Math.PI) Console.WriteLine("d - Пи");
if (str is "314159") Console.WriteLine("str похоже на Пи");
}


Константой считаются:
- целые или числа с плавающей запятой,
- символьные и строковые литералы,
- логические значения true и false,
- значения перечислений,
- имя объявленного поля const или null.

Внимание! Значение, заложенное в шаблон, должно быть константой времени компиляции. Если оно находится в файле appsettings.json, в БД или в статическом поле только для чтения, инициализируемом при запуске, вы не сможете использовать его в шаблоне. Команда C# пока не ослабила это ограничение.

Шаблон типа
Проверяет, имеет ли выражение заданный тип, и в случае успеха связывает значение с новой переменной этого типа. Он заменяет старый алгоритм проверки типа с приведением типов:
static void TypePattern(Shape shape) {
if (shape is Circle c1)
Console.WriteLine($"фигура – круг радиуса {c1.Radius}");

if (shape is Rectangle r && r.Width == r.Height)
Console.WriteLine($"фигура – квадрат со стороной {r.Width}");
}

Шаблон типа оправдывает себя в операторах switch для типов, не имеющих общего базового класса. BCL полон таких ситуаций. Вот небольшой пример: подсчёт элементов произвольной последовательности за O(1), если возможно:
public static int Count<T>(this IEnumerable<T> seq) {
switch (seq) {
case Array a: return a.Length;
case ICollection<T> c: return c.Count;
case IReadOnlyCollection<T> c: return c.Count;
case IEnumerable<T> _: return seq.Count(); // не null
default: return 0; // null
}
}

Порядок имеет значение. Компилятор отклонит операторы case, если обнаружит их недостижимость, например, если ICollection<T> будет перед List<T>.

Шаблон определения и шаблон типа
В спецификации языка C# это два разных шаблона.
1. Шаблон определения shape is Circle c проверяет тип во время выполнения и объявляет новую переменную c этого типа.
2. Шаблон типа shape is Circle проверяет только тип и ничего не объявляет. До C#9 для этого случая приходилось писать Circle _.

Оба варианта отклоняют null. Это свойство делает шаблон объявления самым чистым способом проверки типа значения, допускающего null, и его преобразования в базовый тип за один шаг:
int? m = 12;
if (m is int num) // совпадает при m.HasValue истинном
Console.WriteLine($"m имеет значение {num}");


Оператор when
Внутри оператора switch ключевое слово when добавляет произвольное логическое условие поверх шаблона. Так можно обойти ограничение на константы времени компиляции:
public static int CountUpTo10<T>(this IEnumerable<T> seq) {
switch (seq) {
case Array a when a.Length <= 10:
return a.Length;
case ICollection<T> c when c.Count <= 10:
return c.Count;
case IReadOnlyCollection<T> c when c.Count <= 10:
return c.Count;
default:
throw new ArgumentException("Слишком большой набор");
}
}

When работает только в switch и не допускается в if, даже в последних версиях C#:
if (seq is Array a when a.Length <= 10) // ошибка компиляции


Продолжение следует…

Источник:
https://blog.ndepend.com/c-pattern-matching-explained/
  • 👍 7
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 →