Переключение оператора Fallthrough ... должно ли это быть разрешено? [закрытый]
насколько я помню, я избегал использования fallthrough оператора switch. На самом деле, я не могу вспомнить, чтобы это когда-либо входило в мое сознание как возможный способ делать вещи, поскольку это было просверлено в моей голове рано, что это было не более чем ошибка в заявлении switch. Однако сегодня я столкнулся с некоторым кодом, который использует его по дизайну, что заставило меня сразу задаться вопросом, что все в сообществе думают о fallthrough оператора switch.
это что-то, что язык программирования должен явно не разрешать (как и C#, хотя он предоставляет обходной путь) или это особенность любого языка, который достаточно силен, чтобы оставить в руках программиста?
Edit: Я не был достаточно конкретен, что я имел в виду под провалом. Я использую этот тип много:
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;
}
таким образом, когда дело 0, 1 он будет делать все, что в операторе switch. Я видел это по дизайну, и я просто не знаю, согласен ли я с тем, что операторы switch должны использоваться таким образом. Я думаю, что первый пример кода является очень полезным и безопасным. Второй кажется довольно опасным.
12 ответов:
Это может зависеть от того, что вы считаете проваливаемся. Я в порядке с такого рода вещами:
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++ FAQ определение "зло"
это обоюдоострый меч. Иногда очень полезно, часто опасно.
когда это хорошо? Если вы хотите, чтобы 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*/, чтобы указать, что это было вашим намерением.
Fallthrough действительно удобная вещь, в зависимости от того, что вы делаете. Рассмотрим этот аккуратный и понятный способ организации вариантов:
switch ($someoption) { case 'a': case 'b': case 'c': // do something break; case 'd': case 'e': // do something else break; }
представьте, что вы делаете это с if / else. Это будет полный бардак.
вы слышали о устройство Даффа? Это отличный пример использования Switch fallthrough.
Это функция, которую можно использовать, и ее можно злоупотреблять, как и почти все языковые функции.
это может быть очень полезно несколько раз, но в целом, no-fallthrough-это желаемое поведение. Прохождение должно быть разрешено, но не подразумеваться.
пример, чтобы обновить старые версии некоторых данных:
switch (version) { case 1: // update some stuff case 2: // update more stuff case 3: // update even more stuff case 4: // and so on }
Я хотел бы другой синтаксис для резервных копий в коммутаторах, что-то вроде, 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
как с чем-нибудь: если используется с осторожностью, это может быть элегантный инструмент.
однако, я думаю, что недостатки более чем оправдывают не использовать его, и, наконец, не позволять ему больше (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 конструкций без каких-либо потерь на мой взгляд.
мое последнее слово: держитесь подальше от ярлыков падающих случаев, как в плохом примере, если вы не поддерживаете устаревший код, где этот стиль используется и хорошо понятен.
мощный и опасный. Самая большая проблема с провалом заключается в том, что он не является явным. Например, если вы сталкиваетесь с часто редактируемым кодом, который имеет переключатель с провалами, как вы знаете, что это намеренно, а не ошибка?
везде, где я его использую, я гарантирую, что он правильно прокомментирован:
switch($var) { case 'first': // fall-through case 'second': i++; break; }
мне не нравится
switch
заявления, чтобы провалиться - это слишком подвержены ошибкам и трудно читать. Единственное исключение-когда несколькоcase
операторы все делают ровно то же самое.Если есть какой-то общий код, который хотят использовать несколько ветвей оператора switch, я извлекаю его в отдельную общую функцию, которая может быть вызвана в любой ветви.
использование провала, как в вашем первом примере, явно нормально, я бы не считал это настоящим провалом.
второй пример опасен и (если не комментировать подробно) неочевиден. Я учу своих студентов не использовать такие конструкции, если они не считают, что стоит посвятить ему блок комментариев, в котором описывается, что это преднамеренное провал, и почему это решение лучше, чем альтернативы. Это препятствует небрежному использованию, но все же делает его допустимым случаи, когда он используется с пользой.
Это более или менее эквивалентно тому, что мы делали в космических проектах, когда кто-то хотел нарушить стандарт кодирования: они должны были подать заявку на разрешение (и я был призван сообщить о постановлении).
мысль падения должна использоваться только тогда, когда она используется в качестве таблицы перехода в блок кода. Если есть какая-либо часть кода с безусловным разрывом перед большим количеством случаев, все группы случаев должны заканчиваться таким образом. Все остальное - "зло".
в некоторых случаях использование fall-throughs является актом лени со стороны программиста - например, они могут использовать серию операторов||, но вместо этого используют серию "всеохватывающих" случаев переключения.
Это, как говорится, я нашел их, чтобы быть особенно полезным, когда я знаю, что В конце концов мне все равно понадобятся параметры (например, в ответе меню), но еще не реализованы все варианты. Аналогично, если вы делаете провал для обоих "a" и "A", я считаю, что существенно чище использовать провал переключателя, чем сложное утверждение if.
вероятно, это вопрос стиля и того, как думают программисты, но я обычно не люблю удалять компоненты языка во имя "безопасности" - вот почему я склоняюсь к C и его вариантам/потомкам больше, чем, скажем, Java. Мне нравится быть в состоянии обезьянничать с указателями и тому подобное, даже когда у меня нет "причины".