Паттерны в is
Представьте сортировочную машину на почтовом складе. В ней прорези разной формы: в одну проходят только плоские конверты, в другую — коробки не тяжелее килограмма. Каждая прорезь задаёт посылке один вопрос: «Подходишь?» Ответ на него — да или нет.
В теме о наследовании вы уже писали v is Truck t: проверка типа и новая переменная за один шаг. Код Truck t после is — это паттерн, та самая прорезь: описание того, каким должно быть значение. Проверку значения по паттерну называют сопоставлением с образцом. В is слева стоит значение, справа паттерн, а результат — true или false.
Паттерны бывают разные, но начнём со знакомого. Такой же паттерн, как Truck t, работает и с числом, упакованным в object:
object box = 42;
if (box is int n)
{
Console.WriteLine(n + 1); // 43
}
if (box is string s)
{
Console.WriteLine(s.ToUpper());
}Давайте разберём программу по строкам:
object box = 42;кладёт число в переменную типаobject, где может лежать значение любого типа.box is int nпроверяет, лежит ли вboxчисло типаint. Лежит, поэтому выражение даётtrue, а число попадает в новую переменнуюnтипаint. Это распаковка с проверкой: без приведения(int)boxи без риска исключения.n + 1складывает обычные числа, и программа печатает43. С самимboxтак нельзя:box + 1не скомпилируется, потому что уobjectнет сложения.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:
string? name = null;
Console.WriteLine(name is null); // True
Console.WriteLine(name is not null); // Falseis null проверяет, что в переменной нет значения. Слово not переворачивает паттерн, поэтому is not null даёт true, когда значение есть.
Проверка name == null дала бы здесь то же самое. Но is null всегда проверяет ровно на null, а тип может определить оператор == по-своему, как это делает запись. Поэтому для проверки на null чаще пишут is.
Паттерны сравнения
Паттерн сравнения сравнивает значение с числом знаками <, <=, > и >=. Паттерны соединяют словами and и or, а словом not переворачивают:
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:
object box = 42;
if (box is int n)
{
Console.WriteLine("int");
}
Console.WriteLine(n); // ✗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.