Почему Switch / Case, а не If / Else If? - PullRequest
60 голосов
/ 22 июня 2009

Этот вопрос в основном указывает на C / C ++, но я думаю, что другие языки также актуальны.

Я не могу понять, почему переключатель / регистр все еще используется вместо if / else if. Мне кажется, что это очень похоже на использование goto, и приводит к тому же типу грязного кода, в то время как те же результаты можно получить с if / else if гораздо более организованно.

Тем не менее, я вижу эти блоки вокруг довольно часто. Распространенное место для их поиска - рядом с циклом сообщений (WndProc ...), в то время как они являются одними из тех мест, где они вызывают самый тяжелый хаос: переменные распределяются по всему блоку, даже когда они не принадлежат (и не могут инициализируется внутри него). Особое внимание должно быть уделено тому, чтобы не бросать перерывы, и так далее ...

Лично я не использую их, и мне интересно, пропускаю ли я что-то?

Являются ли они более эффективными, чем если бы / еще? Продолжаются ли они по традиции?

Ответы [ 15 ]

159 голосов
/ 22 июня 2009

Подводя итог моему первому посту и комментариям - есть несколько преимуществ заявления switch по сравнению с утверждением if / else:

  1. Код уборщика. Код с несколькими цепочками if / else if ... выглядит запутанным и сложным в обслуживании - switch обеспечивает более чистую структуру.

  2. Производительность. Для плотных case значений компилятор генерирует таблицу переходов, для разреженно-двоичного поиска или серии if / else, поэтому в худшем случае switch так же быстр, как if / else, но обычно быстрее. Хотя некоторые компиляторы могут аналогичным образом оптимизировать if / else.

  3. Порядок испытаний не имеет значения. Чтобы ускорить серию тестов if / else, нужно сначала поставить более вероятные случаи. С switch / case программисту не нужно думать об этом.

  4. По умолчанию может быть где угодно. С if / else регистр по умолчанию должен быть в самом конце - после последнего else. В switch - default может быть где угодно, где программист сочтет это более подходящим.

  5. Общий код. Если вам нужно выполнить общий код для нескольких случаев, вы можете пропустить break, и выполнение будет «провалено» - чего вы не можете достичь с помощью if / else. (Рекомендуется размещать специальный комментарий /* FALLTHROUGH */ для таких случаев - lint распознает его и не жалуется, без этого комментария он действительно жалуется, так как забыл break).

Спасибо всем комментаторам.

31 голосов
/ 22 июня 2009

Ну, одна из причин - ясность ....

если у вас есть переключатель / регистр, то выражение не может измениться .... т.е.

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 одинаковыми" или "почему они разные"?

16 голосов
/ 22 июня 2009

помните, что case / select обеспечивает дополнительную гибкость:

  • состояние оценивается один раз
  • достаточно гибок для создания таких вещей, как устройство Даффа
  • Fallthrough (он же случай без перерыва)

, а также выполняется намного быстрее (через таблицу переходов / поиска) * исторически

11 голосов
/ 23 июня 2009

Также помните, что операторы 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;
}
9 голосов
/ 22 июня 2009

Если есть много случаев, оператор switch выглядит чище.

Также хорошо, когда у вас есть несколько значений, для которых вы хотите одно и то же поведение - просто использовать несколько выражений "case", которые переходят на одну реализацию, гораздо проще, чем if (this || that || someotherthing | | ...)

6 голосов
/ 22 июня 2009

Это также может зависеть от вашего языка - например, некоторые переключатели языков работают только с числовыми типами, поэтому он экономит вам время, когда вы работаете с перечисляемым значением, числовыми константами ... и т.д ...

If (day == DAYOFWEEK_MONDAY) {
    //...
}
else if (day == DAYOFWEEK_TUESDAY) {
    //...
}
//etc...

Или немного легче читать ...

switch (day) {
    case DAYOFWEEK_MONDAY :
        //...
    case DAYOFWEEK_TUESDAY :
        //...
    //etc...
}
4 голосов
/ 22 июня 2009

На самом деле оператор switch подразумевает, что вы работаете над чем-то, что является более или менее перечислением, которое дает вам мгновенную подсказку о том, что происходит.

Тем не менее, переключение на enum на любом языке OO, вероятно, могло бы быть закодировано лучше - и серия if / else с одним и тем же значением стиля "enum" была бы по меньшей мере столь же плохой и даже хуже при передаче значения.

4 голосов
/ 22 июня 2009

Clarity. Как я сказал здесь , подсказка, что else if проблематична, -

частота, с которой ELSE IF является используется гораздо более ограниченным образом чем это разрешено синтаксисом. Это кувалда гибкости, позволяя совершенно не связаны условия для тестирования. Но это обычно используется, чтобы ударить мух СЛУЧАЙ, сравнивая одно и то же выражение с альтернативными значениями ...

Это уменьшает читаемость код. Поскольку структура позволяет вселенная условной сложности, читатель должен держать больше возможности в виду при разборе ИЛИ ЕСЛИ, чем при разборе CASE.

4 голосов
/ 22 июня 2009

Параметр switch / case обычно оптимизируется более эффективно, чем if / else if / else, но иногда (в зависимости от языка и компилятора) переводится в простые выражения if / else if / else

Лично я считаю, что операторы switch делают код более читабельным, чем набор операторов if; при условии, что вы следуете нескольким простым правилам. Правила, которые вы, вероятно, должны соблюдать даже в ситуациях if / else if / else, но это опять-таки мое мнение.

Эти правила:

  • Никогда, никогда, не иметь более одной линии на вашем блоке переключателей. Вызовите метод или функцию и сделайте свою работу там.
  • Всегда проверяйте на предмет поломки / падения.
  • Исключить пузыри.
3 голосов
/ 25 июня 2009

с учетом того, что все внутри коммутатора имеет эквивалентную область видимости, вы всегда можете добавить свою логику кейса в другой блок {}, например, так ...

switch( thing ) {
    case ONETHING: {
        int x; // local to the case!
        ...
        }
        break;
    case ANOTHERTHING: {
        int x; // a different x than the other one
        }
        break;
}

.. теперь я не говорю, что это красиво. Просто поместите это как нечто, что возможно , если вам абсолютно необходимо изолировать что-то в одном случае от другого.

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

Хорошо, последняя мысль о переключателях: если функция содержит более пары переключателей, возможно, пришло время провести рефакторинг вашего кода. Если функция содержит вложенных переключателей, возможно, это ключ к переосмыслению вашего дизайна =)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...