Инструкция switch
В прошлом уроке цепочка if и else if выбирала ветку по условиям. Часто эти условия похожи друг на друга: одну и ту же переменную сравнивают с разными значениями.
Представьте лифт в офисном здании. На панели по кнопке на каждый этаж: на первом холл, на третьем кафе, на остальных офисы. Вы нажимаете «3», и лифт едет сразу на третий этаж, не останавливаясь на каждом, чтобы спросить, ваш ли это.
Цепочка else if похожа на лифт, который всё-таки останавливается на каждом этаже: она снова и снова сравнивает одну и ту же переменную. Инструкция switch работает как панель лифта: вычисляет значение один раз и переходит к ветке с таким же значением.
int floor = 3;
switch (floor)
{
case 1:
Console.WriteLine("Lobby");
break;
case 3:
Console.WriteLine("Cafe"); // Cafe
break;
default:
Console.WriteLine("Offices");
break;
}Строки case 1:, case 3: и default: называют метками: каждая отмечает, где начинается её ветка.
Давайте пройдём по программе:
switch (floor)вычисляетfloorи получает3.- Метка
case 1:не совпадает с3, и её код не выполняется. - Метка
case 3:совпадает, поэтому выполняется код под ней: программа выводитCafe. breakзавершаетswitch, и программа переходит к коду после закрывающей скобки.
При floor = 1 программа выведет Lobby, а любое число, кроме 1 и 3, даст Offices.
Метки case и default
Код под меткой называют веткой, как и в if. В switch два вида меток.
Метка case
После слова case стоит значение и двоеточие, а ниже — код ветки. Метка case проверяет, равно ли значение в скобках switch её значению.
Значение после case должно быть известно ещё при компиляции: число, символ, строка в кавычках или константа, объявленная через const. Обычную переменную там компилятор не примет. Два case с одинаковым значением тоже не скомпилируются.
Метка default
Ветка default выполняется, когда не подошёл ни один case, как else в цепочке if. Обычно её ставят последней.
Если ни один case не подошёл, а ветки default нет, не выполняется ничего. Программа идёт дальше, к коду после закрывающей скобки switch.
Ветка должна закончиться
В C# каждая ветка обязана выйти из switch, и в этой теме для этого служит break. Выполнение не может перетечь из одной ветки в следующую: если ветка заканчивается без break, программа не компилируется. Правило касается и последней ветки, даже default.
Об этом часто спрашивают на собеседовании. В C, C++ и Java ветка без break молча продолжается кодом следующей ветки, и забытый break превращается в ошибку, которую трудно найти. C# такое продолжение запрещает, поэтому забытый break находит компилятор.
Несколько меток у одной ветки
У правила всё же есть оговорка. Если под меткой нет ни одной инструкции, она относится к ветке ниже. Так несколько значений получают общий код:
int day = 6;
switch (day)
{
case 6:
case 7:
Console.WriteLine("weekend"); // weekend
break;
default:
Console.WriteLine("workday");
break;
}Разберём, что происходит:
dayравен6, и выполнение начинается с меткиcase 6:.- Под ней нет инструкций: метки
case 6:иcase 7:стоят над одной веткой. - Программа выводит
weekend, аbreakзавершаетswitch.
Здесь аналогия с лифтом перестаёт работать. За кнопкой лифта стоит один этаж, а у ветки switch может быть несколько меток, и все они ведут в одно место.
switch по строке
Значением в switch может быть не только число, но и строка. Тогда в метках case стоят строки в кавычках:
string command = "stop";
switch (command)
{
case "start":
Console.WriteLine("Starting");
break;
case "stop":
Console.WriteLine("Stopping"); // Stopping
break;
default:
Console.WriteLine("Unknown command");
break;
}Строки сравниваются целиком и с учётом регистра. Если в command окажется "Stop" с заглавной буквы, ни один case не подойдёт, и программа выведет Unknown command.
Частая ошибка
В ветке забыли break:
int level = 1;
switch (level)
{
case 1:
Console.WriteLine("beginner"); // ✗
case 2:
Console.WriteLine("advanced");
break;
}error CS0163: Control cannot fall through from one case label ('case 1:') to anotherПрограмма не компилируется. Сообщение значит: «выполнение не может перейти от одной метки case к другой». Ветка case 1: заканчивается без break, и компилятор не даёт ей продолжиться кодом ветки case 2:. Чтобы программа скомпилировалась, допишите break после Console.WriteLine("beginner").
Главное
switchвычисляет значение один раз и выполняет ветку с совпадающимcase, а если такой нет — веткуdefault.- После
caseстоит значение, известное при компиляции: число, символ, строка или константаconst. - Каждая ветка заканчивается
break: без него программа не компилируется. - Несколько меток подряд без инструкций между ними ведут к одной ветке.