Выбор switch

Оператор switch предоставляет возможность выбрать один из нескольких вариантов алгоритма. Как правило, количество вариантов гораздо больше двух, потому что в противном случае проще использовать оператор if/else. В принципе, цепочка операторов if/else позволяет во многих случаях (но не во всех) получить эквивалент switch. Важной особенностью switch является то, что все варианты выбираются (идентифицируются) на основе значения выражения целочисленного типа, как правило, переменной.

В обобщенном виде оператор switch выглядит так:

switch ( выражение )
{
   case константное выражение : инструкции [break; ]
   ...
   [ default : инструкции ] 
}

Заголовок оператора начинается с ключевого слова switch. После него в круглых скобах обязательно задается выражение. Блок с фигурными скобками также обязательный.

Целочисленные значения, которые могут получаться при вычислении выражения, следует указывать в виде констант после ключевого слова case. Под константой понимается литерал любого из целых типов, например, int (10, 123), ushort (символы 'A', 's', '*' и т.д.) или элементы перечисления. Вещественные числа, переменные или выражения здесь запрещены.

Таких case-вариантов может быть много, но может и не быть вовсе — это обозначено полукруглыми скобками с индексом opt(n). Все варианты должны иметь уникальные константы (без повторений).

Для каждой альтернативы, задекларированной с помощью case, после двоеточия должна быть написана инструкция, которая и будет выполняться в случае, если значение выражения равно соответствующей константе. Как обычно, инструкция может быть простой инструкцией или составной. Кроме того, допустимо написать несколько простых инструкций, не заключая их в фигурные скобки — они при этом все равно выполнятся как группа (составная инструкция).

После этих одной или нескольких инструкций может быть написан оператор перехода break.

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

При отсутствии break продолжают выполняться инструкции следующей ветки или нескольких веток case, то есть до первого встретившегося break или окончания блока switch. Это называется "проваливанием" (fall-through).

Таким образом, оператор switch не только позволяет разделить поток выполнения алгоритма на несколько альтернатив, но и скомбинировать их, что недоступно для оператора if. С другой стороны, в операторе switch, в отличие от if, нельзя выбрать диапазон значений в качестве условия активации альтернатив.

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

Последовательность, в которой перечислены case-константы и default, может быть произвольной.

Даже если пока не предусмотрено алгоритма для ветки default, рекомендуется делать её в явном виде, пустой, т.е. содержащей break. Пустой default напомнит вам и другим программистам, что прочие варианты существуют, но считаются неважными, так как в противном случае ветка default должна была бы сигнализировать ошибку.

Несколько вариантов case с разными константами может быть указано один под другим (или слева направо) без инструкций, но последний из них должен иметь инструкцию. Такие совмещенные case-ы обозначены на схеме индексом (i).

Вот самый простой и бесполезный оператор switch:

switch(0)
{
}

Рассмотрим более сложный пример с различными режимами (StmtSelectionSwitch.mq5). В нем оператор switch помещен внутрь цикла, чтобы показать, как его работа зависит от значений управляющей переменной i.

for(int i = 0i < 7i++)
{
   double factor = 1.0;
   
   switch(i)
   {
      case -1:
         Print("-1: Never hit");
         break;
      case 1:
         Print("Case 1");
         factor = 1.5;
         break;
      case 2// fall-through, no break (!)
         Print("Case 2");
         factor *= 2;
      case 3// same statements for 3 and 4
      case 4:
         Print("Case 3 & 4");
         {
            double local_var = i * i;
            factor *= local_var;
         }
         break;
      case 5:
         Print("Case 5");
         factor = 100;
         break;
      default:
         Print("Default: "i);
   }
   
   Print(factor);
}

Вариант -1 не выполнится, потому что цикл меняет переменную i от 0 до 6 (включительно). Когда i равно 0, сработает ветка default. Она же перехватит управление при i равном 6. Все остальные возможные значения i распределяются по соответствующим директивам case. При этом после case 2 нет инструкции break, в связи с чем код для вариантов 3 и 4 выполнится в довесок к 2 (в таких случаях всегда рекомендуется оставлять комментарий, что это сделано намеренно).

Случаи 3 и 4 имеют общий блок инструкций. Но еще тут важно отметить, что если требуется объявить локальную переменную внутри одного из case-вариантов, нужно заключить инструкции во вложенный составной блок ('{...}'). Здесь таким образом определена переменная local_var.

Обратите внимание, в случае default отсутствует инструкция break. Она избыточна, потому что default в данном случае написан в последнюю очередь. Однако многие программисты советуют вставлять break в конце любого варианта, даже последнего, потому что он может перестать быть последним в процессе последующих модификаций кода, и тогда легко забыть добавить break — а это, вероятно, приведет к ошибке в логике программы.

Если в switch нет варианта default, и выражение в заголовке не совпадает ни с одной из case-констант, весь оператор switch пропускается.

В результате выполнения скрипта мы получим следующие сообщения в журнале:

Default: 0
1.0
Case 1
1.5
Case 2
Case 3 & 4
8.0
Case 3 & 4
9.0
Case 3 & 4
16.0
Case 5
100.0
Default: 6
1.0