Рубріки: Теория

Зачем нужна конструкция switch-case в C# и как с ней работать

Денис Бородовский

Операторы switch и case нужны для управления сложными условными операциями и операциями ветвления. Они нередко применяются в современной С#-разработке, и любой уважающий себя программист должен уметь ими пользоваться.

Содержание статьи:

1. Введение

2. Принцип работы конструкции switch-case в C#

2.1 Пример использования оператора switch без блока default

2.2 Вложение конструкций

2.3 Применение строк в операторе switch в С#

2.4 Работа с return

3. Конструкция goto case

4. Применение переключателя на практике

5. C#7.0: использование операторов диапазона и when

6. Изменения в C# 8.0: контекст выражения

7. Версия C# 9.0: выражения переключения с сопоставлением с образцом

8. Паттерны switch в C#

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

8.2 Паттерн кортежей

8.3 Позиционный паттерн

8.4 Реляционный и логический паттерны

9. Итоги

Введение

С момента своего появления в C# конструкция switchcase стала его неотъемлемой частью. Она используется для передачи строки или числового значения (заданного переменной или выражением) ряду констант. Другими словами, это инструмент управления выбором, позволяющий проверять переменную на равенство. 

Синтаксис оператора switch в C#:

switch (expression) {
       case constantA:
             Instructions);
             break;
       case constantB:
             Instructions);
             break;
       default:
             Instructions);
             break;
}

Принцип работы конструкции switch-case в C#

Определенное выражение, стоящее в скобках за ключевым словом switch, последовательно сравнивается со значениями констант, помеченных оператором case. В случае совпадения программа переходит к соответствующему оператору и выполняет блок кода, стоящий за ним. 

Во всех отдельных блоках case после инструкций в круглых скобках ставится один из операторов перехода: break, goto case, return или throw. При его использовании другие участки конструкции, помеченные ключевым словом case, выполняться не будут.

Если ни один оператор case не сработал, выполнение программы переходит к оператору default — с его помощью мы можем отработать ситуацию, когда совпадение не найдено.

Пример использования оператора switch без блока 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;
}

Вложение конструкций

Также конструкция switchcase может быть вложена в другую управляющую конструкцию: 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 в С#

Кроме чисел в 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;
   }
      }
}

Работа с return

Также из переключателя 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("Недопустимый код");
    }
}

При использовании switchcase следует учитывать несколько моментов:

  • Необходимо вводить целочисленные константы как простые числовые литералы (3, 423), символьные литералы, как обычно, указываются в одинарных кавычках ('a', 'D') и строковые литералы в — двойных («red», «black», «white»).
  • Спецификация блока по умолчанию не является обязательной.
  • Все блоки case должны заканчиваться оператором прерывания. Исключение составляют блоки, не предоставляющие никаких инструкций и использование оператора goto.

Конструкция goto case

Сочетание ключевых слов 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;
}

Применение переключателя на практике

Рассмотрим старую задачу из учебников по программированию для лучшего понимания конструкции switchcase в C#.

Предположим, по заказу отдела кадров вашей компании, вам нужно написать метод, возвращающий уровень квалификации соискателей, желающих устроиться на работу, на основании их опыта. Нам необходимо пометить их таким образом:

  • 0 лет опыта: Неопытные (Inexperienced);
  • 1 или 2 года опыта: Новичок (Beginner);
  • 3-5 лет опыта: Средний уровень (Intermediate);
  • Опыт работы более 5 лет: Эксперт (Expert).

Теперь разберемся с возможными решениями этой задачи:

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 для каждого отрицательного значения — явно не очень хорошее решение. В такой ситуации лучше преобразовать switchcase в блок ifelse.

C# 7.0: использование операторов диапазона и when

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: контекст выражения

Начиная с 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: выражения переключения с сопоставлением с образцом

В версии C# 9.0. появилось обновление, включающее в себя новый функционал, позволяющий более естественным образом использовать операторы <, >, <= и >= в выражении switchcase:

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, а вся конструкция более лаконична и удобочитаема, чем в предыдущем примере.

Паттерны switch в С#

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

Этот паттерн необходим для сравнения заданного выражения со значениями определенных свойств объекта. Например, создадим класс пользователя:

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

Останні статті

Обучение Power BI – какие онлайн курсы аналитики выбрать

Сегодня мы поговорим о том, как выбрать лучшие курсы Power BI в Украине, особенно для…

13.01.2024

Work.ua назвал самые конкурентные вакансии в IТ за 2023 год

В 2023 году во всех крупнейших регионах конкуренция за вакансию выросла на 5–12%. Не исключением…

08.12.2023

Украинская IT-рекрутерка создала бесплатный трекер поиска работы

Unicorn Hunter/Talent Manager Лина Калиш создала бесплатный трекер поиска работы в Notion, систематизирующий все этапы…

07.12.2023

Mate academy отправит работников в 10-дневный оплачиваемый отпуск

Edtech-стартап Mate academy принял решение отправить своих работников в десятидневный отпуск – с 25 декабря…

07.12.2023

Переписки, фото, история браузера: киевский программист зарабатывал на шпионаже

Служба безопасности Украины задержала в Киеве 46-летнего программиста, который за деньги устанавливал шпионские программы и…

07.12.2023

Как вырасти до сеньйора? Девелопер создал популярную подборку на Github

IT-специалист Джордан Катлер создал и выложил на Github подборку разнообразных ресурсов, которые помогут достичь уровня…

07.12.2023