Перейти к содержимому
Теория · Перечисления, записи и паттерны · урок 6 из 8

Паттерны в is

Представьте сортировочную машину на почтовом складе. В ней прорези разной формы: в одну проходят только плоские конверты, в другую — коробки не тяжелее килограмма. Каждая прорезь задаёт посылке один вопрос: «Подходишь?» Ответ на него — да или нет.

В теме о наследовании вы уже писали v is Truck t: проверка типа и новая переменная за один шаг. Код Truck t после is — это паттерн, та самая прорезь: описание того, каким должно быть значение. Проверку значения по паттерну называют сопоставлением с образцом. В is слева стоит значение, справа паттерн, а результат — true или false.

Паттерны бывают разные, но начнём со знакомого. Такой же паттерн, как Truck t, работает и с числом, упакованным в object:

C#
object box = 42;

if (box is int n)
{
    Console.WriteLine(n + 1); // 43
}

if (box is string s)
{
    Console.WriteLine(s.ToUpper());
}

Давайте разберём программу по строкам:

  1. object box = 42; кладёт число в переменную типа object, где может лежать значение любого типа.
  2. box is int n проверяет, лежит ли в box число типа int. Лежит, поэтому выражение даёт true, а число попадает в новую переменную n типа int. Это распаковка с проверкой: без приведения (int)box и без риска исключения.
  3. n + 1 складывает обычные числа, и программа печатает 43. С самим box так нельзя: box + 1 не скомпилируется, потому что у object нет сложения.
  4. box is string s даёт false: в box лежит не строка. Тело второго if не выполняется, и программа больше ничего не печатает.

Паттерн типа

Код int n после is называют паттерном типа. Как и Truck t, он делает две вещи за один шаг:

  • проверяет, что значение имеет тип int;
  • если это так, кладёт значение в новую переменную n, уже с типом int.

Без паттерна понадобились бы проверка и отдельное приведение: if (box is int) { int k = (int)box; … }. Тип пришлось бы писать дважды.

Как вы уже знаете, null не подходит ни к одному паттерну типа: если в box лежит null, проверка box is string s даёт false. Поэтому внутри if переменная из паттерна точно не null.

Здесь аналогия с прорезью перестаёт работать. Прорезь только пропускает посылку дальше, а паттерн int n ещё и отдаёт вам значение в новой переменной.

Проверка на null

Паттерном может быть и конкретное значение. Самое частое из них — null:

C#
string? name = null;
Console.WriteLine(name is null);     // True
Console.WriteLine(name is not null); // False

is null проверяет, что в переменной нет значения. Слово not переворачивает паттерн, поэтому is not null даёт true, когда значение есть.

Проверка name == null дала бы здесь то же самое. Но is null всегда проверяет ровно на null, а тип может определить оператор == по-своему, как это делает запись. Поэтому для проверки на null чаще пишут is.

Паттерны сравнения

Паттерн сравнения сравнивает значение с числом знаками <, <=, > и >=. Паттерны соединяют словами and и or, а словом not переворачивают:

C#
int age = 15;
Console.WriteLine(age is >= 13 and <= 19); // True
Console.WriteLine(age is < 0 or > 120);    // False
Console.WriteLine(age is not 15);          // False

Что делает каждое слово:

  • and — значение должно подойти к обоим паттернам;
  • or — хотя бы к одному из них;
  • not — значение не должно подходить к паттерну.

Слова and и or соединяют паттерны для одного и того же значения, поэтому age пишут один раз. Код age is >= 13 && <= 19 не скомпилируется: && соединяет два полных условия, а <= 19 само по себе не условие.

Справа от знака сравнения стоит число или константа, а не обычная переменная. На age is > limit компилятор ответит ошибкой CS9135: ему нужно значение, известное при компиляции.

Частая ошибка

Переменную из паттерна использовали после if:

C#
object box = 42;
if (box is int n)
{
    Console.WriteLine("int");
}
Console.WriteLine(n); // ✗
TEXT
error CS0165: Use of unassigned local variable 'n'

Программа не компилируется. Переменная n получает значение, только когда проверка прошла, а после if компилятор не знает, прошла ли она. Используйте n внутри if: там значение есть всегда.

Главное

  • Паттерн описывает, каким должно быть значение, а проверка is с паттерном даёт true или false.
  • box is int n проверяет тип и сразу кладёт значение в n, а пользоваться n можно только там, где проверка точно прошла.
  • is null и is not null проверяют ровно на null, а сам null не подходит ни к одному паттерну типа.
  • Паттерны сравнения соединяют словами and и or и переворачивают словом not: age is >= 13 and <= 19.