Вот основной синтаксис оператора switch:
```csharp
switch (expression)
{
case constant1:
// код, выполняемый, если выражение равно constant1
break;
case constant2:
// код, выполняемый, если выражение равно constant2
break;
// можно добавить любое количество case
...
default:
// код, выполняемый, если выражение не равно ни одной из констант
}
```
**Break и default**:
В каждом блоке case обычно требуется оператор break для прерывания выполнения оператора switch. Блок default не является обязательным, но обычно используется для обработки случаев, когда выражение не совпадает ни с одним из вариантов case.
**Switch и типы данных**: В выражении switch можно использовать любой тип данных, который допускает операцию сравнения на равенство: числовые типы, строки, перечисления (enum), и т.д.
Оператор switch в C# использует оператор \== для сравнения, а не метод Equals.
`switch` требует **константных значений** (❌ Нельзя использовать переменные в `case`)
**Switch и pattern matching**: С версии C# 7.0 в switch можно использовать сопоставление с образцами (pattern matching). Это позволяет более гибко проверять выражение на соответствие различным условиям или типам.
На тему pattern matching есть отдельная одноименная папка в C# папке и одноименная заметка.
### Switch expressions
С версии C# 8.0 в язык было добавлено так называемое "выражение switch" (switch expression), которое представляет собой более сжатую и выразительную форму оператора switch. В отличие от традиционного оператора switch, выражение switch предполагает возврат значения.
В `switch`-выражениях нельзя использовать `{ ... }`, но можно вызывать методы.
Вот базовый синтаксис выражения switch:
```csharp
var result = expression switch
{
pattern1 => result1,
pattern2 => result2,
_ => defaultResult // обработка всех остальных случаев
};
```
Здесь expression - это выражение, которое сопоставляется с различными образцами (pattern1, pattern2, и т.д.). Если expression совпадает с образцом, то возвращается соответствующий результат (result1, result2, и т.д.).
Посмотрим на пример:
Примечание: Здесь также применяется Type pattern
```csharp
var shape = GetShape(); // Возвращает объект типа Shape
var area = shape switch
{
Circle c => Math.PI * c.Radius * c.Radius, // Если shape является Circle, вычисляем площадь круга
Rectangle r => r.Height * r.Width, // Если shape является Rectangle, вычисляем площадь прямоугольника
_ => 0 // Во всех остальных случаях возвращаем 0
};
```
Как видно из примера, выражение switch позволяет избавиться от необходимости писать break, так как после выполнения каждого блока case автоматически прекращается выполнение всего выражения switch.
Также обратите внимание, что вместо блока default используется образец \_, который соответствует любому значению.
В выражении switch можно использовать все те же Pattern Matching, что и в операторе switch.
### Производительность
Оператор switch эффективнее с точки зрения производительности, чем длинная цепочка операторов if-else if-else, особенно когда имеется много вариантов для проверки.
Когда вы используете оператор switch в C#, компилятор генерирует так называемую прыжковую таблицу (jump table), которая эффективно представляет собой словарь, где каждому значению, которое может быть проверено, соответствует ссылка на код, который должен быть выполнен при сопоставлении этого значения.
прыжковая таблица в контексте оператора switch обычно представляет собой массив, где индексы представляют значения переменной switch, а значения в массиве - это места (адреса), куда нужно перейти в коде. Это значит, что операция доступа к элементу в этом массиве выполняется за время O(1) - то есть время выполнения этой операции не зависит от количества элементов в таблице. Это гораздо быстрее, чем последовательно проверять каждое условие в цепочке if-else, что занимает время O(n), где n - количество условий.
Для ситуаций, когда значения не могут быть прямым индексом в массиве (например, при работе со строками), используется хэш-таблица, которая также предлагает быстрый доступ к значениям. В таком случае, значение переменной switch преобразуется в хэш, который затем используется для поиска соответствующего места в коде.
Таким образом, даже если у вас есть switch с 100 случаями, и значение соответствует последнему случаю, нет необходимости проверять все предыдущие 99 условий - код сразу переходит к нужному блоку.
Применение прыжковой таблицы (jump table) или хэш-таблицы для оптимизации switch становится невозможным или неэффективным в следующих ситуациях:
* Сложные условия: Если условия в switch становятся сложнее простого равенства значений (например, проверки диапазонов значений, проверки, включающие вызовы функций), компилятор не может применить прыжковую таблицу или хэш-таблицу. В этих случаях используется последовательное сопоставление, подобное цепочке if-else-if.
* Числовые значения с большим разрывом: Если используются целочисленные значения, между которыми есть большой разрыв, создание прыжковой таблицы становится неэффективным, так как она должна была бы включать все промежуточные значения.
* Нелинейное распределение значений: Если значения, используемые в case, не распределены равномерно, прыжковая таблица может быть неэффективной. Хэш-таблица также может стать неэффективной, если большое количество значений приводит к одинаковым хэшам, что приводит к коллизиям.
* Объекты со сложными функциями хеширования: Если switch используется с типами объектов, у которых функции хеширования сложны или дороги (высокие затраты вычислительных ресурсов), использование хэш-таблицы может быть неэффективным.