Зачем нужна конструкция switch-case в C# и как с ней работать
Операторы switch
и case
нужны для управления сложными условными операциями и операциями ветвления. Они нередко применяются в современной С#-разработке, и любой уважающий себя программист должен уметь ими пользоваться.
Содержание статьи:
2. Принцип работы конструкции switch-case в C#
2.1 Пример использования оператора switch без блока default
2.3 Применение строк в операторе switch в С#
4. Применение переключателя на практике
5. C#7.0: использование операторов диапазона и when
6. Изменения в C# 8.0: контекст выражения
7. Версия C# 9.0: выражения переключения с сопоставлением с образцом
8.4 Реляционный и логический паттерны
С момента своего появления в C# конструкция switch
–case
стала его неотъемлемой частью. Она используется для передачи строки или числового значения (заданного переменной или выражением) ряду констант. Другими словами, это инструмент управления выбором, позволяющий проверять переменную на равенство.
Синтаксис оператора switch в C#:
switch (expression) { case constantA: Instructions); break; case constantB: Instructions); break; default: Instructions); break; }
Определенное выражение, стоящее в скобках за ключевым словом switch
, последовательно сравнивается со значениями констант, помеченных оператором case
. В случае совпадения программа переходит к соответствующему оператору и выполняет блок кода, стоящий за ним.
Во всех отдельных блоках case
после инструкций в круглых скобках ставится один из операторов перехода: break
, goto
case
, return
или throw
. При его использовании другие участки конструкции, помеченные ключевым словом case
, выполняться не будут.
Если ни один оператор case
не сработал, выполнение программы переходит к оператору default
— с его помощью мы можем отработать ситуацию, когда совпадение не найдено.
В случае, если блок default
отсутствует и нет совпадений со значением выражения, то никаких действий не выполняется, например:.
int x; x = 7; switch (x) { case 1: Console.WriteLine ("one"); break; case 2: Console.WriteLine ("two"); break; case 3: Console.WriteLine ("three"); break; case 4: Console.WriteLine ("four"); break; case 5: Console.WriteLine ("five"); break; }
Также конструкция switch
–case
может быть вложена в другую управляющую конструкцию: if
, switch
, for
, while
, do..while
, foreach
и др. Причем количество уровней вложения неограниченно, например:
switch (n) { case 1: switch(z) { case 0: printf("ноль"); break; case 1: process(n,z); } break; case 2: …. }
Кроме чисел в switch
могут использоваться строки символов, например:
class Prog{ static void Main(string[] args){ string day; // название дня недели day = Console.ReadLine();// 1. Ввод названия дня недели switch (day){ // 2. Вывод порядкового номера дня по его названию case "Monday": Console.WriteLine("Numberofday = 1"); break; case "Tuesday": Console.WriteLine("Numberofday = 2"); break; case "Wednesday": Console.WriteLine("Numberofday = 3"); break; case "Thursday": Console.WriteLine("Numberofday = 4"); break; case "Friday: Console.WriteLine("Numberofday = 5"); break; case "Saturday": Console.WriteLine("Numberofday = 6"); break; case "Sunday": Console.WriteLine("Numberofday= 7"); break; default: Console.WriteLine("неправильный ввод"); break; } } }
Также из переключателя switch
можно вернуть определенные значения при помощи return
, например:
static int Sel(int z, int x, int y) { switch (z) { case 1: return x + y; case 2: return x - y; case 3: return x * y; default: throw new ArgumentException("Недопустимый код"); } }
При использовании switch
–case
следует учитывать несколько моментов:
3, 423
), символьные литералы, как обычно, указываются в одинарных кавычках ('a'
, 'D'
) и строковые литералы в — двойных («red»
, «black»
, «white»
).case
должны заканчиваться оператором прерывания. Исключение составляют блоки, не предоставляющие никаких инструкций и использование оператора goto
.Сочетание ключевых слов goto case
позволяет не прерывать поиск, а после выполнения текущего блока case
переходить к следующему, не используя оператор прерывания break
. Например:
int num = 1; switch (num) { case 1: Console.WriteLine ("case1"); goto case 3; // переходим к case 3 case 2: Console.WriteLine ("case2"); break; case 3: Console.WriteLine ("case3"); break; default: Console.WriteLine ("default"); break; }
Рассмотрим старую задачу из учебников по программированию для лучшего понимания конструкции switch
–case
в C#.
Предположим, по заказу отдела кадров вашей компании, вам нужно написать метод, возвращающий уровень квалификации соискателей, желающих устроиться на работу, на основании их опыта. Нам необходимо пометить их таким образом:
Теперь разберемся с возможными решениями этой задачи:
private string GetExperiencelevel_OldSwitchStatement(int yearsOfExperience){ string level; switch (yearsOfExperience){ case 0: level = “Inexperienced”; break; case 1: case 2: level = “Beginner”; break; case 3: case 4: case 5: level = “Intermediate”; default: level = “Expert”; break; } return level; }
Вышеупомянутый метод использует оператор switch
для проверки значения переданной ему переменной yearsOfExperience
. Затем он возвращает строковое значение, представляющее уровень опыта соискателя. Обратите внимание, что в операторе switch
вы должны заполнить каждую метку case
. Кроме того, не забудьте добавить ключевое слово break
там, где вам нужно выйти из условного оператора.
В нашем примере все значения, превышающие 5, попадут в категорию «Эксперт». Но поскольку их слишком много, мы помещаем этот случай в блок оператора default
.
В целом код выглядит простым и легким для чтения, но очень негибким для проверки диапазона значений. Почему?
Рассмотрим другой пример. Предположим, вы хотите добавить проверку на отрицательные значения в приведенном выше операторе switch
. В этом случае добавление метки case
для каждого отрицательного значения — явно не очень хорошее решение. В такой ситуации лучше преобразовать switch
–case
в блок if
–else
.
Microsoft выпустила C# 7.0 в 2017 году. Одна из эволюционных функций этой версии, связанных с оператором switch
, — возможность указывать условие в блоке case
с помощью ключевого слова when
, при котором код выглядит чище и обрабатывается необходимый нам диапазон значений:
private string GetExperiencelevel_CSharp7(int yearsOfExperience){ string level; switch (yearsOfExperience){ case 0: level = “Inexperienced”; break; case: int i when i > 0 && i <= 2 level = “Beginner”; break; case : int i when i > 2 && i <= 5 level = “Intermediate”; default: level = “Expert”; break; } return level; }
Начиная с C# 8.0, мы можем использовать switch
в контексте выражения. Теперь мы можем использовать лямбда-выражения с возможностью применения в них операторов диапазона. Невооруженным глазом видно, что код ниже, более компактный, по сравнению с предыдущим:
private string GetExperiencelevel_CSharp8(int yearsOfExperience){ string level = yearsOfExperience switch{ int i when i == 0 => “Inexperienced”, int i when i > 0 && i <= 2 => “Beginner”, int i when i > 2 && i <= 5 => “Intermediate”, _=> “Expert” }; return level; }
Обратите внимание, что ключевое слово switch
появляется после переменной yearsOfExperience
. Каждая строка с выражением заканчивается запятой. И еще один интересный момент: в выражении вы не найдете ни одного ключевого слова case
или break
.
Такой подход позволяет писать значительно меньше строк кода. Фактически, мы можем упростить наш пример, возвращая результат выражения напрямую, превратив весь блок в метод, основанный на выражении:
private string GetExperiencelevel_CSharp8(int yearsOfExperience){ string level = yearsOfExperience switch{ int i when i == 0 => “Inexperienced”, int i when i > 0 && i <= 2 => “Beginner”, int i when i > 2 && i <= 5 => “Intermediate”, _=> “Expert” };
Конечно, чем меньше кода, тем лучше, но вам необязательно использовать самый минималистичный вариант. Помните, что удобочитаемость имеет значение, особенно если вы работаете в команде, где другие люди должны понимать то, что вы написали.
В версии C# 9.0. появилось обновление, включающее в себя новый функционал, позволяющий более естественным образом использовать операторы <
, >
, <=
и >=
в выражении switch
–case
:
private string GetExperiencelevel_CSharp9(int yearsOfExperience){ string result = yearsOfExperience switch{ 0 => “Inexperienced”, > 0 and <= 2 => “Beginner”, > 2 and i <= 5 => “Intermediate”, _=> “Expert” }; return result; }
Обратите внимание, что в приведенном выше участке кода не используется ключевое слово when
, а вся конструкция более лаконична и удобочитаема, чем в предыдущем примере.
Этот паттерн необходим для сравнения заданного выражения со значениями определенных свойств объекта. Например, создадим класс пользователя:
class User{ public string Name { get; set; } // с именем public string Status { get; set; } // со статусом пользователя public string Language { get; set; } // с используемым языком }
Затем в методе, основываясь на значениях статуса и языка, выведем пользователю сообщение при помощи паттерна свойств:
static string GetMessage(User i) => i switch{ { Language: "english" } => "Hi!", { Language: "russian", Status: "admin" } => "Привет, админ!", { Language: "french" } => "Salut!", { } => "undefined" };
Внутри фигурных скобок в теле метода указаны свойства и через двоеточие — их значение. С последним и сравнивается свойство передаваемого объекта.
Такой паттерн нужен для сравнения условного выражения со значениями кортежей. В следующем примере — кортеж с названием языка и временем суток:
static string GetWelcom(string lang, string daytime) => (lang, daytime) switch{ ("russian", "morning") => "Доброе утро", ("russian", "evening") => "Добрый вечер", ("italian", "morning") => "Buongiorno", ("italian", "evening") => "Buonasera", _ => "Нello" };
Кортеж создается из двух значений, передаваемых в метод. Затем в нашей конструкции в круглых скобках определяются значения, которым должны соответствовать элементы кортежа.
Позиционный паттерн используется у типов с методом деконструктора. Для примера сделаем класс с деталями нашего сообщения:
class MsgDetails{ public string Language { get; set; } public string DateTime { get; set; } public string Status { get; set; } public void Deconstruct(out string lang, out string datetime, out string status){ lang = Language; datetime = DateTime; status = Status; } }
Теперь используем позиционный паттерн и в зависимости от значений объекта MsgDetails
возвратим определенное сообщение:
static string GetWelcome(MsgDetails details) => details switch { ("russian", "morning") => "Доброе утро", ("russian", "evening") => "Добрый вечер", ("italian", "morning") => "Buongiorno", ("italian", "evening") => "Buonasera", ("italian", "evening") => "Buonasera", _ => "Нello" };
Рассматриваемый паттерн очень похож на пример с кортежами, но тут вместо кортежа в конструкцию switch
мы передаем объект MsgDetails
. Теперь через метод Deconstruct
можно получить список выходных параметров в виде кортежа, а затем сравнивать их со значениями, как в предыдущем примере.
Реляционный паттерн нужен для сравнения передаваемых в конструкцию значений при помощи операторов сравнения. Рассмотрим простой пример расчета суммы процентов исходя из суммы вклада, применив этот паттерн:
static decimal Calc(decimal result) { return result switch { <= 0 => 0, // при значении result больше либо равно 0 - вернётся 0 < 20000 => result * 0.02m, // при значении result меньше 20000, вернётся result * 0.02m < 100000 => result * 0.1m, // при значении result меньше 100000, вернётся result * 0.1m _ => result * 0.2m // иначе вернется result * 0.2m }; }
Логический паттерн позволяет использовать логические операторы (and
и or
) для объединения операций сравнения. Пример его использования мы видели в главе, освещающей изменения в С#9.
В статье мы рассмотрели различные конструкции переключателей в C# и разобрались, как записать их быстрее и проще. Также познакомились с полезными паттернами конструкции switch
и с тем, для чего они нужны. Надеемся, вам было полезно!
Ловите видео для закрепления материала:
Видео: Уроки C# (C sharp) — Оператор Switch
Прокси (proxy), или прокси-сервер — это программа-посредник, которая обеспечивает соединение между пользователем и интернет-ресурсом. Принцип…
Согласитесь, было бы неплохо соединить в одно сайт и приложение для смартфона. Если вы еще…
Повсеместное распространение смартфонов привело к огромному спросу на мобильные игры и приложения. Миллиарды пользователей гаджетов…
В перечне популярных чат-ботов с искусственным интеллектом Google Bard (Gemini) еще не пользуется такой популярностью…
Скрипт (англ. — сценарий), — это небольшая программа, как правило, для веб-интерфейса, выполняющая определенную задачу.…
Дедлайн (от англ. deadline — «крайний срок») — это конечная дата стачи проекта или задачи…