- •4. Лекция: Операторы языка c#
- •Оператор присваивания
- •Семантика присваивания
- •Операции "упаковать" и "распаковать" - boxing и unboxing
- •Блок, или составной оператор
- •Пустой оператор
- •Операторы выбора
- •Оператор if
- •Оператор switch
- •Операторы перехода
- •Оператор goto
- •Операторы break и continue
- •Оператор return
- •Операторы цикла
- •Оператор for
- •Циклы While
- •Цикл foreach
- •Специальные операторы
- •Оператор yield
- •Операторы try, catch, finally
- •Операторы checked и unchecked
- •Оператор fixed
- •Оператор lock
- •Проект Statements
- •Альтернатива и разбор случаев
- •Вычисление сумм, произведений и рекуррентные соотношения
- •Рекуррентные вычисления
- •Бесконечность и компьютеры. Вычисления с точностью ε
- •Проекты
-
Оператор switch
Частным, но важным случаем выбора из нескольких вариантов является ситуация, при которой выбор варианта определяется значениями некоторого выражения. Соответствующий оператор C#, унаследованный от C++, но с небольшими изменениями в синтаксисе, называется оператором switch. Вот его синтаксис:
switch(выражение)
{
case константное_выражение_1: [операторы_1 оператор_перехода_1]
…
case константное_выражение_K: [операторы_K оператор_перехода_K]
[default: операторы_N оператор_перехода_N]
}
Ветвь default может отсутствовать. Заметьте: по синтаксису допустимо, чтобы после двоеточия следовала пустая последовательность операторов, а не последовательность, заканчивающаяся оператором перехода. Константные выражения в case должны иметь тот же тип, что и switch-выражение.
Семантика оператора switch чуть запутана. Вначале вычисляется значение switch-выражения. Затем оно поочередно в порядке следования case сравнивается на совпадение с константными выражениями. Как только достигнуто совпадение, выполняется соответствующая последовательность операторов case-ветви. Поскольку последний оператор этой последовательности является оператором перехода (чаще всего это оператор break), обычно он завершает выполнение оператора switch. Использование операторов перехода - это плохая идея. Таким оператором может быть оператор goto, передающий управление другой case-ветви, которая, в свою очередь, может передать управление еще куда-нибудь, получая блюдо "спагетти" вместо хорошо структурированной последовательности операторов. Семантика осложняется еще и тем, что case-ветвь может быть пустой последовательностью операторов. Тогда в случае совпадения константного выражения этой ветви со значением switch-выражения будет выполняться первая непустая последовательность очередной case-ветви. Если значение switch-выражения не совпадает ни с одним константным выражением, то выполняется последовательность операторов ветви default, если же таковой ветви нет, то оператор switch эквивалентен пустому оператору.
Полагаю, что оператор switch - это самый неудачный оператор языка C# как с точки зрения синтаксиса, так и семантики. Неудачный синтаксис порождает запутанную семантику, являющуюся источником плохого стиля программирования. Понять, почему авторов постигла неудача, можно, оправдать - нет. Дело в том, что оператор унаследован от С++, где его семантика и синтаксис еще хуже. В языке C# синтаксически каждая case-ветвь должна заканчиваться оператором перехода (забудем на минуту о пустой последовательности), иначе возникнет ошибка периода компиляции. В языке С++ это правило не является синтаксически обязательным, хотя на практике применяется та же конструкция с конечным оператором break. При его отсутствии управление "проваливается" в следующую case-ветвь. Конечно, профессионал может с успехом использовать этот трюк, но в целом ни к чему хорошему это не приводит. Борясь с этим, в C# потребовали обязательного включения оператора перехода, завершающего ветвь. Гораздо лучше было бы, если бы последним оператором мог быть только оператор break, как следствие, его можно было бы не писать, и семантика стала бы прозрачной - при совпадении значений двух выражений выполняются операторы соответствующей case-ветви, при завершении которой завершается и оператор switch.
Еще одна неудача в синтаксической конструкции switch связана с существенным ограничением, накладываемым на case-выражения, которые могут быть только константным выражением. Уж если изменять оператор, то гораздо лучше было бы использовать синтаксис и семантику Visual Basic, где в case-выражениях допускается список, каждое из выражений которого может задавать диапазон значений.
Разбор случаев - это часто встречающаяся ситуация в самых разных задачах. Применяя оператор switch, помните о недостатках его синтаксиса, используйте его в правильном стиле. Заканчивайте каждую case-ветвь оператором break, но не применяйте goto.
Содержательный пример применения оператора switch подробно рассмотрен в лекции 2. Рассмотрим еще один показательный пример, в котором вычисляется арифметическое выражение с двумя аргументами.
/// <summary>
/// Разбор случаев с использованием списков выражений
/// </summary>
/// <param name="operation">операция над аргументами</param>
/// <param name="arg1">первый аргумент бинарной операции</param>
/// <param name="arg2">второй аргумент бинарной операции</param>
/// <param name="result">результат бинарной операции</param>
public void ExprResult(string operation, double arg1, double arg2,
ref double result)
{
switch (operation)
{
case "+":
case "Plus":
case "Плюс":
result = arg1 + arg2;
break;
case "-":
case "Minus":
case "Минус":
result = arg1 - arg2;
break;
case "*":
case "Mult":
case "Умножить":
result = arg1 * arg2;
break;
case "/":
case "Divide":
case "Div":
case "разделить":
case "Делить":
result = arg1 / arg2;
break;
default:
result = 0;
break;
}
}//ExprResult
Обратите внимание: знак операции над аргументами можно задавать разными способами, что демонстрирует возможность задания списка константных выражений в ветвях оператора switch.