Паттерны в switch
В теме об условиях ветви выражения switch сравнивали значение с конкретными числами: 1 => "Lobby". Для оценки по баллам так пришлось бы писать ветвь на каждое число от 0 до 100. Но в ветви можно поставить любой паттерн из прошлого урока.
Вернёмся к сортировочной машине. Её прорези стоят в ряд, и посылка едет по ленте мимо них сверху вниз. Посылка проваливается в первую прорезь, куда пролезает. До нижних прорезей она уже не доезжает, даже если пролезла бы и туда.
Выражение switch с паттернами работает так же:
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) по ветвям:
< 0 or > 100не подходит:72лежит между0и100.>= 90не подходит,>= 80тоже.>= 70подходит, и всё выражение получает значение"C".- Ветвь
_уже не проверяется: выбор заканчивается на первой подходящей ветви.
Ветви проверяются по порядку
Это правило вы знаете из темы об условиях: ветви проверяются сверху вниз, и выигрывает первая подходящая. С конкретными числами его было трудно заметить: значение равно только одному из них.
Паттерны же пересекаются. Число 95 подходит сразу к трём ветвям: >= 90, >= 80 и >= 70. Выигрывает первая из них, поэтому Grade(95) даёт A. Порядок ветвей — часть смысла программы: ставьте узкие ветви выше широких.
В виджете метод Kind вызывают трижды, с разными числами. Нажимайте «Следующий шаг» и следите, на какой ветви останавливается каждое из них.
1Console.WriteLine(Kind(5));2Console.WriteLine(Kind(50));3Console.WriteLine(Kind(500));4 5string Kind(int n) => n switch6{7 < 10 => "small",8 < 100 => "medium",9 _ => "large",10};
Main вызывает Kind(5) и ждёт результат, чтобы его напечатать.
Число 5 подходит и к < 10, и к < 100, но проверка остановилась на первой ветви. До _ дошло только 500: оно не подошло ни к одной ветви выше.
На собеседовании любят спросить, что будет, если значение подходит к нескольким ветвям. Ответ: срабатывает первая сверху, а остальные даже не проверяются.
Компилятор по-прежнему следит, чтобы для каждого значения нашлась ветвь, и выдаёт предупреждение CS8509, если это не так. С диапазонами он разбирается сам: n switch { < 0 => "-", >= 0 => "+" } охватывает все числа, и ветвь _ здесь не нужна.
Какие паттерны ставят в ветви
Паттерны сравнения вы уже видели в Grade. Ещё два вида встречаются в ветвях постоянно.
Паттерн типа
Ветвь может проверить тип и сразу дать переменную, как is int n:
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:
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, поэтому первая ветвь не подходит, а вторая подходит.
Частая ошибка
Широкую ветвь поставили выше узкой:
int score = 95;
string grade = score switch
{
>= 70 => "C",
>= 90 => "A", // ✗
_ => "F",
};
Console.WriteLine(grade);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. - Частично пересекающиеся ветви компилируются, и значение достаётся первой из них.