Зачем переключать / Case, а не If / Else If?



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



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



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



лично я избегаю их использовать, и мне интересно, что я чего-то не хватает?



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

680   15  

15 ответов:

Резюмируя мой первоначальный пост и комментарии-есть несколько преимуществ 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 */ для таких случаев-Линт распознает его и не жалуется, без этого комментария он жалуется, так как это распространенная ошибка забыть break).

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

Ну, одна из причин заключается в ясности....

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

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

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

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

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

также помните, что операторы 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;
}

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

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

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

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

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

switch (day) {
    case DAYOFWEEK_MONDAY :
        //...
    case DAYOFWEEK_TUESDAY :
        //...
    //etc...
}

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

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

те правила:

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

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

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

это снижает читаемость код. Поскольку структура позволяет Вселенная условной сложности, читатель должен держать больше возможности в виду при анализе Иначе если чем при разборе дела.

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

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

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

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

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

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

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

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

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

операторы Switch могут быть оптимизированы для скорости, но могут занимать больше памяти, если значения case распределены по большому количеству значений.

Если/else, как правило, медленно, так как каждое значение должно быть проверено.

Smalltalker может отклонить как switch, так и 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.

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

и некоторые, как время выполнения является еще одной причиной.

довольно уверен, что они компилируются к тем же вещам, что и if/else if, но я найти switch/case легче читать, когда есть больше, чем 2 или 3 else s.

Comments

    Ничего не найдено.