Пропадание оператора switch… должно ли это быть разрешено? [закрыто]

120

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

Является ли это чем-то, что язык программирования должен явно запрещать (как это делает C #, хотя и предоставляет обходной путь), или это особенность любого языка, которая достаточно мощна, чтобы оставить ее в руках программиста?

Изменить: я не был достаточно конкретен в том, что имел в виду под провалом. Я часто использую этот тип:

    switch(m_loadAnimSubCt){
        case 0:
        case 1:
            // Do something
            break;
        case 2:
        case 3:
        case 4:
            // Do something
            break;
   }

Однако меня беспокоит что-то вроде этого.

   switch(m_loadAnimSubCt){
        case 0:
        case 1:
            // Do something, but fall through to the other cases
            // after doing it.

        case 2:
        case 3:
        case 4:
            // Do something else.
            break;
   }

Таким образом, всякий раз, когда case равен 0, 1, он будет делать все в инструкции switch. Я видел это по дизайну, и я просто не знаю, согласен ли я с тем, что операторы switch должны использоваться таким образом. Я думаю, что первый пример кода очень полезен и безопасен. Второй вариант кажется опасным.

Fostah
источник
51
Не согласен по поводу неконструктивного. 27 голосов за этот вопрос говорят о том, что есть и другие, которые тоже так думают.
Wes Modes
2
«неконструктивный» - старая и несколько расплывчатая причина. В настоящее время такие вопросы можно и нужно пометить для закрытия, поскольку они основаны исключительно на мнениях. SO предназначен для ответов на вопросы о коде или дизайне, а не «каково мнение« сообщества »[что бы то ни было] о моем мнении о Feature X». Programmers.SE подойдет лучше, но даже в этом случае ценность такого широкого и основанного на мнении вопроса все еще весьма сомнительна.
underscore_d
1
Существуют очень четкие случаи использования провалов, поэтому не знаю, почему они основаны на мнении. Согласен, этот вопрос немного неясен, но его не следовало закрывать как неконструктивный. Может быть, просто отредактировано, чтобы быть более ясным: «Когда используется провал?» На что есть четкий ответ. Хорошие тексты на C ++ проходят через это, это сбивает с толку новых программистов (или даже более опытных программистов с других языков), поэтому кажется хорошим вопросом для SO. В самом деле, это похоже на прототип хорошего SO-вопроса. Даже если вопрос был не идеальным.
eric

Ответы:

89

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

switch (value)
{
  case 0:
    result = ZERO_DIGIT;
    break;

  case 1:
  case 3:
  case 5:
  case 7:
  case 9:
     result = ODD_DIGIT;
     break;

  case 2:
  case 4:
  case 6:
  case 8:
     result = EVEN_DIGIT;
     break;
}

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

И обратите внимание, что я использую определение «зла» из часто задаваемых вопросов C ++.

Фред Ларсон
источник
Я согласен с вами: я не считаю ваш пример провальным, и если есть случай, когда я хочу выполнить несколько блоков кода через провал, вероятно, есть лучший способ сделать это.
Дэйв ДюПлантис,
10
+1 просто за то определение «зло». Я позаимствую это, спасибо ;-)
Йоахим Зауэр
Я понимаю вашу точку зрения, и вы правы, но ваш пример действительно плох. Никто не должен так проверять контрольные цифры. Скнр, в любом случае твой ответ правильный.
Тим Бют, 01
Показательный пример: это как раз то, что C # запрещает. Вероятно, по какой-то причине :-)
Джоуи
К сожалению , определение зла , кажется, пошли в автономном режиме
HopefullyHelpful
57

Это обоюдоострый меч. Иногда это очень полезно, но часто опасно.

Когда это хорошо? Если вы хотите, чтобы все 10 дел обрабатывались одинаково ...

switch (c) {
  case 1:
  case 2:
            ... Do some of the work ...
            /* FALLTHROUGH */
  case 17:
            ... Do something ...
            break;
  case 5:
  case 43:
            ... Do something else ...
            break;
}

Мне нравится одно правило: если вы когда-нибудь сделаете что-нибудь необычное, исключив перерыв, вам понадобится четкий комментарий / * FALLTHROUGH * /, чтобы указать, что это было вашим намерением.

Джон М
источник
3
+1! Я согласен с тем, что иногда это правильный ответ, и Я ДВОЙНО согласен с тем, что, когда он задуман, его следует прокомментировать. (В обзоре кода я бы СДЕЛАЛ комментарий другого разработчика непустым провалом по дизайну. Не то, чтобы такое случается; мы магазин C #, где это не поддерживается :)
Джон Руди,
4
Я сам использую комментарий / * FALL THROUGH * / в своем коде там, где это необходимо. =]
strager
2
Если вы используете PC-Lint, вам нужно вставить / * - fallthrough * /, иначе он жалуется.
Steve Melnikoff
1
тем не менее, если вы хотите прояснить, что поведение является преднамеренным, вы также можете if(condition > 1 && condition <10) {condition = 1}; switch(...сохранить свои кейсы (для ясного взгляда), плюс все могут увидеть, что вы действительно хотели, чтобы эти кейсы вызывали одно и то же действие.
Марк
1
Это все еще ужасное кодирование. Первый случай должен вызывать функцию, второй случай должен вызывать ту же функцию, а затем вызывать вторую функцию.
BlueRaja - Дэнни Пфлугхофт
21

Вы слышали об устройстве Даффа ? Это отличный пример использования провала переключения.

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

tzot
источник
Узнавайте что-то новое каждый день - спасибо! Я сочувствую бедным разработчикам, которые идут на такие извращения.
Джастин Р.
Ух ты, это то, над чем ты можешь подумать.
Фостах,
6
Обратите внимание на процитированный Даффом комментарий в этой статье: «Этот код является своего рода аргументом в этой дискуссии, но я не уверен, за или против».
Джон Картер,
Устройство Даффа явно злое
Марджета
21

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

switch ($someoption) {
  case 'a':
  case 'b':
  case 'c':
    // Do something
    break;

  case 'd':
  case 'e':
    // Do something else
    break;
}

Представьте, что вы делаете это с помощью if / else. Был бы беспорядок.

Лукас Оман
источник
1
Я считаю, что этот тип провала очень полезен
Митчел Селлерс
если «сделать что-нибудь» - это больше, чем просто мелочь, то переключатель будет скорее беспорядком, чем if / else. С if / else везде ясно, какая переменная проверяется. Даже этот небольшой пример не выглядел бы так плохо с if / else в моих глазах
grenix 05
10

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

Пример для обновления старых версий некоторых данных:

switch (version) {
    case 1:
        // Update some stuff
    case 2:
        // Update more stuff
    case 3:
        // Update even more stuff
    case 4:
        // And so on
}
Питер Мортенсен
источник
6

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

switch(myParam)
{
  case 0 or 1 or 2:
    // Do something;
    break;
  case 3 or 4:
    // Do something else;
    break;
}

Примечание: это уже возможно с перечислениями, если вы объявите все случаи в своем перечислении с помощью флагов, верно? Это тоже не так уж плохо; случаи могут (должны?) уже быть частью вашего перечисления.

Может быть, это будет хороший случай (без каламбура) для свободного интерфейса с использованием методов расширения? Что-то вроде, эээ ...

int value = 10;
value.Switch()
  .Case(() => { /* Do something; */ }, new {0, 1, 2})
  .Case(() => { /* Do something else */ } new {3, 4})
  .Default(() => { /* Do the default case; */ });

Хотя это, вероятно, еще менее читабельно: P

Эрик ван Бракель
источник
1
Мне очень нравится первое предложение. Это очень хорошо читается и экономит несколько строк кода. Второе заняло минуту, чтобы осмыслить мой мозг, но имеет смысл, но похоже, что это действительно беспорядок.
Fostah
1
Да, я тоже так подумал, это было просто случайное пердение ума, которое я подавил, пытаясь найти другой способ переключения с использованием существующих языковых конструкций.
Эрик ван Бракель,
Просмотрите этот вопрос, и мне это действительно нравится! У меня была идея, как улучшить читаемость этого, но SO не позволяет мне публиковать многострочные комментарии :( Кто-то должен реализовать это так же, как POC, выглядит забавно реализовать и использовать (:
Виктор Элиас
5

Как и все остальное: при осторожном использовании он может стать изящным инструментом.

Тем не менее, я считаю , что недостатки больше , чем обосновывают не использовать его, и , наконец , чтобы не допустить его больше (C #). Среди проблем:

  • перерыв легко "забыть"
  • для разработчиков кода не всегда очевидно, что пропущенный разрыв был намеренным

Хорошее использование переключателя / провала корпуса:

switch (x)
{
case 1:
case 2:
case 3:
 Do something
 break;
}

Бааааад использование переключателя / провала корпуса:

switch (x)
{
case 1:
    Some code
case 2:
    Some more code
case 3:
    Even more code
    break;
}

На мой взгляд, это можно переписать с использованием конструкций if / else без каких-либо потерь.

Мое последнее слово: держитесь подальше от ярлыков провалов, как в плохом примере, если вы не поддерживаете устаревший код, в котором этот стиль используется и хорошо понят.

steffenj
источник
3
Вы можете переписать большинство языковых конструкций, используя if () и goto ... но это не оправдывает отказ от явной конструкции.
Shog9,
2
Я знаю, но конструкции switch / case, которые явно используют «case 1: some code case 2: some more code case 3: final code break;» можно и нужно переписать с помощью if / else if (мое мнение).
steffenj
1
Обратите внимание, что switch может быть во много раз быстрее, чем список if-elses. Switch использует таблицу переходов, или, когда это невозможно, его условные переходы будут структурированы в виде дерева решений вместо линейного списка условных переходов. Хорошо структурированные вложенные операторы If-else будут в лучшем случае одинаково быстрыми.
Jochem Kuijpers
4

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

Везде, где я его использую, я гарантирую, что он правильно прокомментирован:

switch($var) {
    case 'first':
        // Fall-through
    case 'second':
        i++;
        break;
 }
Дэн Халтон
источник
2
Намерение очевидно, если нет кода для случая «первый». Если вы там кодируете, а также хотите запустить код для case 'second', тогда комментарий важен. Но я бы все равно избежал этого сценария.
Джоэл Кохорн,
1
@Joel - Полностью согласен. В моем магазине люди оставляли комментарии в таких случаях, и я думаю, что это снижает удобочитаемость. Однако, если там есть код, вставьте дополнительный комментарий.
Фред Ларсон,
Что мы подразумеваем под «неявным». Вот почему мы ломаемся; Другие языки делают это. Это проблема только для людей, которые не выучили свой язык по порядку. C # требует этого для случая ffs по умолчанию, без причины для imho.
mckenzm
3

Использование провала, как в вашем первом примере, явно нормально, и я бы не стал считать его настоящим провалом.

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

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

Воутер ван Оойен
источник
2

Я не люблю, когда мои switchутверждения проваливаются - они слишком подвержены ошибкам и их трудно читать. Единственное исключение - когда caseвсе несколько операторов точно выполняют то же самое.

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

Мэтт Диллард
источник
1

В некоторых случаях использование провалов является проявлением лени со стороны программиста - они могут использовать серию || операторов, например, но вместо этого используют серию универсальных переключателей.

При этом я обнаружил, что они особенно полезны, когда я знаю, что в конечном итоге мне все равно понадобятся параметры (например, в ответе меню), но я еще не реализовал все варианты. Точно так же, если вы делаете провал как для «a», так и для «A», я считаю, что гораздо проще использовать переключатель «провал», чем составной оператор if.

Вероятно, это вопрос стиля и того, как думают программисты, но я обычно не люблю удалять компоненты языка во имя «безопасности» - вот почему я больше склоняюсь к C и его вариантам / потомкам, чем, скажем, Ява. Мне нравится работать с указателями и тому подобным, даже когда у меня для этого нет "причины".

кроличий садок
источник
Строго говоря. Операторы switch переходят через заданное значение к множеству определенных участков кода. Хотя компилятор, скорее всего, уловит такие вещи, для оператора switch над серией операторов или есть значение скорости и правильности. Если p равно 0, или p равно 1, или p равно 2. Мне действительно приходилось оценивать, было ли p равно 0, а p было 1, а не переходить к месту p = 2 в коде. Что оказалось идентично p0 и p1. Но мне никогда не приходилось их проверять.
Tatarize
1

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

Все остальное - «зло».

BCS
источник