Перейти к содержимому
Теория · Условия и циклы · урок 5 из 13

Выражение switch

В прошлом уроке инструкция switch выбирала, какой код выполнить. Часто нужно другое: по номеру этажа получить его название и сохранить в переменной. С инструкцией switch для этого понадобились бы переменная, присваивание в каждой ветке и break после каждого присваивания.

Вернёмся к лифту. Рядом с кнопками висит табличка: «1 — холл, 3 — кафе, остальные — офисы». Табличка ничего не делает, она только отвечает: назовите этаж — получите название. Выражение switch устроено как такая табличка: по значению оно выдаёт результат.

C#
int floor = 3;

string name = floor switch
{
    1 => "Lobby",
    3 => "Cafe",
    _ => "Offices",
};

Console.WriteLine(name); // Cafe

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

  1. floor switch { … } берёт значение floor, то есть 3, и ищет подходящую строку таблички.
  2. Строка 1 => "Lobby" не подходит: 3 не равно 1.
  3. Строка 3 => "Cafe" подходит, и всё выражение получает значение "Cafe".
  4. Это значение попадает в переменную name, и программа выводит Cafe.

Из чего состоит выражение switch

Каждая строка вида 3 => "Cafe" называется ветвью выражения. Вся конструкция складывается из четырёх частей:

  • значение перед словом switch — то, что проверяют, здесь floor;
  • ветви в фигурных скобках: слева от => значение, справа результат, а после ветви запятая;
  • ветвь _ — «всё остальное»: она подходит к любому значению, как default в инструкции switch;
  • точка с запятой после закрывающей скобки: весь код от string name до ; — одна инструкция.

Ветви проверяются сверху вниз, и выигрывает первая подходящая. Поэтому _ ставят последней. Ветвь ниже неё не сработала бы никогда, и компилятор такой код не примет.

Все результаты должны подходить переменной слева. Здесь каждая ветвь даёт строку, и ветвь с числом 3 => 42 не скомпилировалась бы.

Выражение и инструкция

Это та же пара, что if и ?:: инструкция выполняет код, а у выражения есть значение. Значение выражения switch можно не сохранять в переменной, а сразу передать в Console.WriteLine:

C#
int day = 7;

Console.WriteLine(day switch
{
    6 => "Saturday",
    7 => "Sunday",
    _ => "workday",
}); // Sunday

Выражение day switch { … } даёт "Sunday", и это значение сразу уходит в Console.WriteLine.

В ветви выражения стоит одно значение, а не инструкции: ни break, ни несколько строк кода туда не поставить. Когда в каждом случае нужно выполнить несколько действий, берут инструкцию switch.

На собеседовании иногда спрашивают, чем выражение switch отличается от инструкции switch. Короткий ответ: инструкция выполняет код ветки, а выражение вычисляет значение, и ветвям не нужен break.

Здесь аналогия с табличкой перестаёт работать. Если строки для этажа на табличке нет, табличка молчит. Выражение switch молчать не может: без подходящей ветви оно останавливает программу.

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

Забыли ветвь _:

C#
int floor = 4;

string name = floor switch // ✗
{
    1 => "Lobby",
    3 => "Cafe",
};

Console.WriteLine(name);
TEXT
warning CS8509: The switch expression does not handle all possible values of its input type (it is not exhaustive). For example, the pattern '0' is not covered.

Программа компилируется, но с этим предупреждением: компилятор заметил, что для 0 и многих других чисел ветви нет. При запуске с floor = 4 ни одна ветвь не подходит, и программа останавливается на этой строке с исключением:

TEXT
Unhandled exception. System.Runtime.CompilerServices.SwitchExpressionException: Non-exhaustive switch expression failed to match its input.

Инструкция switch без default в таком случае не выполнила бы ничего. Выражению же обязательно нужно значение для name, а взять его негде. Добавьте ветвь _ => "Offices", и исчезнут и предупреждение, и исключение.

Главное

  • Выражение switch выбирает результат по значению: floor switch { 1 => "Lobby", _ => "Offices" }.
  • Ветви проверяются сверху вниз; ветвь _ подходит к любому значению и стоит последней.
  • Инструкция switch выполняет код ветки, а выражение switch даёт значение, и break ему не нужен.
  • Без подходящей ветви программа останавливается с исключением SwitchExpressionException, и компилятор заранее предупреждает об этом: CS8509.