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

Паттерны в switch

В теме об условиях ветви выражения switch сравнивали значение с конкретными числами: 1 => "Lobby". Для оценки по баллам так пришлось бы писать ветвь на каждое число от 0 до 100. Но в ветви можно поставить любой паттерн из прошлого урока.

Вернёмся к сортировочной машине. Её прорези стоят в ряд, и посылка едет по ленте мимо них сверху вниз. Посылка проваливается в первую прорезь, куда пролезает. До нижних прорезей она уже не доезжает, даже если пролезла бы и туда.

Выражение switch с паттернами работает так же:

C#
Console.WriteLine(Grade(95)); // A
Console.WriteLine(Grade(72)); // C
Console.WriteLine(Grade(-5)); // invalid

string Grade(int score) => score switch
{
    < 0 or > 100 => "invalid",
    >= 90 => "A",
    >= 80 => "B",
    >= 70 => "C",
    _ => "F",
};

Давайте пройдём вызов Grade(72) по ветвям:

  1. < 0 or > 100 не подходит: 72 лежит между 0 и 100.
  2. >= 90 не подходит, >= 80 тоже.
  3. >= 70 подходит, и всё выражение получает значение "C".
  4. Ветвь _ уже не проверяется: выбор заканчивается на первой подходящей ветви.

Ветви проверяются по порядку

Это правило вы знаете из темы об условиях: ветви проверяются сверху вниз, и выигрывает первая подходящая. С конкретными числами его было трудно заметить: значение равно только одному из них.

Паттерны же пересекаются. Число 95 подходит сразу к трём ветвям: >= 90, >= 80 и >= 70. Выигрывает первая из них, поэтому Grade(95) даёт A. Порядок ветвей — часть смысла программы: ставьте узкие ветви выше широких.

В виджете метод Kind вызывают трижды, с разными числами. Нажимайте «Следующий шаг» и следите, на какой ветви останавливается каждое из них.

ВживуюПервая подходящая ветвьшаг 1 из 12 · рамок: 1

Main вызывает Kind(5) и ждёт результат, чтобы его напечатать.

Следите, на какой ветви останавливается каждое число

Число 5 подходит и к < 10, и к < 100, но проверка остановилась на первой ветви. До _ дошло только 500: оно не подошло ни к одной ветви выше.

На собеседовании любят спросить, что будет, если значение подходит к нескольким ветвям. Ответ: срабатывает первая сверху, а остальные даже не проверяются.

Компилятор по-прежнему следит, чтобы для каждого значения нашлась ветвь, и выдаёт предупреждение CS8509, если это не так. С диапазонами он разбирается сам: n switch { < 0 => "-", >= 0 => "+" } охватывает все числа, и ветвь _ здесь не нужна.

Какие паттерны ставят в ветви

Паттерны сравнения вы уже видели в Grade. Ещё два вида встречаются в ветвях постоянно.

Паттерн типа

Ветвь может проверить тип и сразу дать переменную, как is int n:

C#
Console.WriteLine(Describe(7));    // int 7
Console.WriteLine(Describe("hi")); // text hi
Console.WriteLine(Describe(true)); // other

string Describe(object x) => x switch
{
    int n => "int " + n,
    string s => "text " + s,
    _ => "other",
};

Переменная n существует только в своей ветви, справа от =>. Значение true не подошло ни к int, ни к string и досталось ветви _.

Паттерн свойства

Паттерн свойства проверяет свойства значения. Паттерн { X: 0 } подходит, если свойство X равно 0:

C#
var p = new Point(0, 5);
Console.WriteLine(p switch
{
    { X: 0, Y: 0 } => "origin",
    { X: 0 } => "on the Y axis",
    { Y: 0 } => "on the X axis",
    _ => "elsewhere",
}); // on the Y axis

record Point(int X, int Y);

Свойства в фигурных скобках перечисляют через запятую, и подойти должны все. Справа от двоеточия может стоять любой паттерн, например { X: > 0 }. У точки (0, 5) свойство Y не равно 0, поэтому первая ветвь не подходит, а вторая подходит.

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

Широкую ветвь поставили выше узкой:

C#
int score = 95;
string grade = score switch
{
    >= 70 => "C",
    >= 90 => "A", // ✗
    _ => "F",
};
Console.WriteLine(grade);
TEXT
error CS8510: The pattern is unreachable. It has already been handled by a previous arm of the switch expression or it is impossible to match.

Программа не компилируется. Любое число от 90 и выше подходит и к >= 70, поэтому ветвь >= 90 не выиграет никогда. Поставьте >= 90 выше >= 70, как в Grade.

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

Частичное пересечение ошибкой не считается. В Grade число 85 подходит к ветвям >= 80 и >= 70: компилятор молча это принимает, а при запуске число достаётся первой из них. Такой порядок проверяете вы сами.

Главное

  • Ветви выражения switch проверяются сверху вниз, и срабатывает первая подходящая.
  • В ветвях стоят любые паттерны: сравнения >= 90, типа int n, свойства { X: 0 }.
  • Ветвь, которую целиком закрывают ветви выше, не компилируется: CS8510.
  • Частично пересекающиеся ветви компилируются, и значение достаётся первой из них.