Почему Switch/Case, а не If/Else If?
Этот вопрос в основном указывается на C/С++, но я думаю, что другие языки также актуальны.
Я не могу понять, почему используется параметр switch/case вместо if/else if. Мне очень нравится использование goto's и приводит к тому же виду беспорядочного кода, в то время как те же результаты могут быть достигнуты с if/else, если более организованным образом.
Тем не менее, я вижу эти блоки довольно часто. Общепринятое место для их нахождения находится рядом с контуром сообщений (WndProc...), тогда как они относятся к местам, когда они поднимают самый тяжелый хаос: переменные разделяются по всему блоку, даже если они не являются подходящими (и не могут быть инициализируется внутри него). Дополнительное внимание нужно уделять не прекращению перерыва, и так далее...
Лично я не использую их, и мне интересно, что я чего-то не хватает?
Являются ли они более эффективными, чем if/else?
Продолжают ли они традиции?
Ответы
Ответ 1
Подводя итоги моей первоначальной публикации и комментариев - существует несколько преимуществ инструкции switch
над выражением if
/else
:
-
Чистый код. Код с несколькими цепями if
/else if ...
выглядит грязным и его трудно поддерживать - switch
дает более чистую структуру.
- Производительность
. Для плотных case
значений компилятор генерирует таблицу перехода, для разреженного бинарного поиска или серии if
/else
, поэтому в худшем случае switch
работает так же быстро, как if
/else
, но обычно быстрее. Хотя некоторые компиляторы также могут оптимизировать if
/else
.
-
Тестовый заказ не имеет значения. Чтобы ускорить серию тестов if
/else
, сначала нужно поставить более вероятные случаи. Программисту switch
/case
не нужно об этом думать.
-
Значение по умолчанию может быть в любом месте. При значении if
/else
значение по умолчанию должно быть в самом конце - после последнего else
. В switch
- default
может быть где угодно, где программист считает его более подходящим.
-
Общий код. Если вам нужно выполнить общий код для нескольких случаев, вы можете опустить break
, и выполнение будет "проваливаться" - чего вы не можете достичь с помощью if
/else
. (Существует хорошая практика разместить специальный комментарий /* FALLTHROUGH */
для таких случаев - lint распознает его и не жалуется, без этого комментария он жалуется, поскольку обходиться с ошибкой забыть break
).
Спасибо всем комментаторам.
Ответ 2
Ну, одна из причин - ясность....
если у вас есть переключатель /case, то выражение не может измениться....
то есть.
switch (foo[bar][baz]) {
case 'a':
...
break;
case 'b':
...
break;
}
тогда как с if/else, если вы пишете по ошибке (или намерению):
if (foo[bar][baz] == 'a') {
....
}
else if (foo[bar][baz+1] == 'b') {
....
}
люди, читающие ваш код, задаются вопросом: "Были ли выражения foo одинаковыми" или "почему они разные"?
Ответ 3
помните, что case/select обеспечивает дополнительную гибкость:
- условие оценивается один раз
- достаточно гибкая, чтобы создавать такие вещи, как устройство Duff
- прохождение (aka case без разрыва)
а также выполняется намного быстрее (через таблицу прыжка/поиска) * исторически
Ответ 4
Также помните, что инструкции switch позволяют продолжить управление потоком, что позволяет вам хорошо комбинировать условия, позволяя вам добавлять дополнительный код для определенных условий, например, в следующий фрагмент кода:
switch (dayOfWeek)
{
case MONDAY:
garfieldUnhappy = true;
case TUESDAY:
case WEDNESDAY:
case THURSDAY:
case FRIDAY:
weekDay = true;
break;
case SATURDAY:
weekendJustStarted = true;
case SUNDAY:
weekendDay = true;
break;
}
Использование здесь if/else
утверждений не будет таким приятным.
if (dayOfWeek == MONDAY)
{
garfieldUnhappy = true;
}
if (dayOfWeek == SATURDAY)
{
weekendJustStarted = true;
}
if (dayOfWeek == MONDAY || dayOfWeek == TUESDAY || dayOfWeek == WEDNESDAY
|| dayOfWeek == THURSDAY || dayOfWeek == FRIDAY)
{
weekDay = true;
}
else if (dayOfWeek == SATURDAY || dayOfWeek == SUNDAY)
{
weekendDay = true;
}
Ответ 5
Если есть много случаев, оператор switch кажется более чистым.
Также хорошо, когда у вас есть несколько значений, для которых вы хотите иметь такое же поведение - просто использование нескольких "случайных" операторов, которые попадают в одну реализацию, намного легче читать, чем if (this || that || someotherthing | |...)
Ответ 6
Это может также зависеть от вашего языка. Например, некоторые языки переключаются только с числовыми типами, поэтому он сохраняет вас при наборе текста, когда вы работаете с перечисляемым значением, числовыми константами... и т.д.
If (day == DAYOFWEEK_MONDAY) {
//...
}
else if (day == DAYOFWEEK_TUESDAY) {
//...
}
//etc...
Или немного легче читать...
switch (day) {
case DAYOFWEEK_MONDAY :
//...
case DAYOFWEEK_TUESDAY :
//...
//etc...
}
Ответ 7
Коммутатор/случай обычно оптимизирован более эффективно, чем if/else if/else, но иногда (в зависимости от языка и компилятора) переводится в простые операторы if/else if/else.
Я лично считаю, что операторы switch делают код более читаемым, чем кучу операторов if; при условии, что вы выполните несколько простых правил. Правила, которые вы, вероятно, должны соблюдать даже для ситуаций if/else if/else, но это снова мое мнение.
Эти правила:
- Никогда, никогда, не более одной строки на вашем блоке коммутатора. Вызовите метод или функцию и выполните свою работу там.
- Всегда проверяйте прохождение патча/случая.
- Исключение пузырьков.
Ответ 8
Clarity. Как я сказал здесь, ключ, который else if
проблематичен, это
частота, с которой ELSE IF используется гораздо более ограниченным образом чем разрешено синтаксисом. Это кувалдой гибкости, разрешая полностью несвязанные условия, подлежащие испытанию. Но это обычно используется для сматывания мух CASE, сравнивая одно и то же выражение с альтернативными значениями...
Это уменьшает читаемость код. Поскольку структура позволяет вселенная условной сложности, читателю необходимо сохранить больше возможности при анализе ELSE IF, чем при разборе CASE.
Ответ 9
На самом деле оператор switch означает, что вы работаете над чем-то более или менее перечислением, которое дает вам мгновенное представление о том, что происходит.
Тем не менее, переключатель на перечислении на любом языке OO, вероятно, может быть лучше закодирован - и ряд значений if/else по одному и тому же значению стиля "enum" будет по крайней мере столь же плохим и даже хуже при передаче смысла.
Ответ 10
обращая внимание на то, что все внутри коммутатора имеет эквивалентную область видимости, вы всегда можете отбросить свою логику вашего дела в другой блок {}, например.
switch( thing ) {
case ONETHING: {
int x; // local to the case!
...
}
break;
case ANOTHERTHING: {
int x; // a different x than the other one
}
break;
}
.. теперь я не говорю это красиво. Просто поместите это там как нечто, что возможно, если вам абсолютно необходимо изолировать что-то в одном случае от другого.
еще одна мысль о проблеме области - кажется хорошей практикой только поставить один переключатель внутри функции, а не многое другое. В этих условиях переменная область не столько беспокоит, что вы обычно имеете дело только с одним случаем выполнения при любом вызове функции.
ok, последняя мысль о коммутаторах: если функция содержит более двух коммутаторов, возможно, потребуется время для реорганизации вашего кода. Если функция содержит вложенные ключи, это, вероятно, ключ к переосмыслению вашей конструкции бит =)
Ответ 11
Случай переключения в основном используется для выбора в программировании. Это не связано с условным выражением как:
Если ваша программа требует только выбора, то почему вы используете блок if/else и увеличиваете усилия по программированию, а также уменьшаете скорость выполнения программы.
Ответ 12
Довольно уверены, что они скомпилируются с теми же вещами, что и if
/else if
, но я считаю, что switch
/case
легче читать, если их больше 2 или 3 else
s.
Ответ 13
Операторы switch могут быть оптимизированы для скорости, но могут занимать больше памяти, если значения case распределены по большим числам значений.
if/else обычно медленны, так как каждое значение необходимо проверить.
Ответ 14
Smalltalker может отклонить оба ключа и if-then-else и может написать что-то вроде: -
shortToLongDaysMap := Dictionary new.
shortToLongDaysMap
at: 'Mon' put: 'Monday';
at: 'Tue' put: 'Tuesday';
at: 'Wed' put: 'Wednesday'
etc etc.
longForm := shortToLongDaysMap at: shortForm ifAbsent: [shortForm]
Это тривиальный пример, но я надеюсь, вы можете увидеть, как этот метод масштабируется для большого числа случаев.
Обратите внимание, что второй аргумент at:IfAbsent:
похож на предложение по умолчанию для оператора case.
Ответ 15
Основная причина этого - удобство и удобочитаемость. Его легко сделать код более читабельным и поддерживаемым с помощью оператора Switch/case, а затем if/else. Поскольку у вас много, если /else, тогда код становится настолько грязным, как гнездо, и его очень сложно поддерживать.
И некоторые, как время выполнения является другой причиной.