Это - тот же синтаксис способом слишком много языков:
switch (someValue) {
case OPTION_ONE:
case OPTION_LIKE_ONE:
case OPTION_ONE_SIMILAR:
doSomeStuff1();
break; // EXIT the switch
case OPTION_TWO_WITH_PRE_ACTION:
doPreActionStuff2();
// the default is to CONTINUE to next case
case OPTION_TWO:
doSomeStuff2();
break; // EXIT the switch
case OPTION_THREE:
doSomeStuff3();
break; // EXIT the switch
}
Теперь все Вы знаете это break
операторы требуются, потому что switch
продолжится к следующему case
когда break
оператор отсутствует. У нас есть пример этого с OPTION_LIKE_ONE
, OPTION_ONE_SIMILAR
и OPTION_TWO_WITH_PRE_ACTION
. Проблема состоит в том, что нам только нужен этот "пропуск к следующему случаю" очень очень очень редко. И очень часто мы помещаем повреждение в конце case
.
Это очень легкий для новичка забыть об этом. И один из моих учителей C даже объяснил это нам, как будто это была ошибка на языке C (не хотите говорить об этом :)
Я хотел бы спросить, существуют ли какие-либо другие языки, которые я не знаю (или забыл о), что переключатель/случай дескриптора как это:
switch (someValue) {
case OPTION_ONE: continue; // CONTINUE to next case
case OPTION_LIKE_ONE: continue; // CONTINUE to next case
case OPTION_ONE_SIMILAR:
doSomeStuff1();
// the default is to EXIT the switch
case OPTION_TWO_WITH_PRE_ACTION:
doPreActionStuff2();
continue; // CONTINUE to next case
case OPTION_TWO:
doSomeStuff2();
// the default is to EXIT the switch
case OPTION_THREE:
doSomeStuff3();
// the default is to EXIT the switch
}
Второй вопрос: там какое-либо историческое значение к тому, почему оно похоже на это в C? Может быть продолжаются к следующему случаю, использовался намного чаще, чем мы используем его в эти дни?
Из этой статьи я могу перечислить некоторые языки, которые не требуют break
-подобного утверждения:
отступление
продолжение
Ваш второй вопрос довольно интересен. Если брать только C, я считаю, что это решение позволяет сохранить целостность языка. Поскольку break
- это jump, он должен быть явно написан.
языков слишком много, и я не могу с уверенностью сказать, что такого языка не существует, при условии, что он является «производным» от C в синтаксисе, потому что другие языки, использующие другой синтаксис и где регистр не «продолжается», естественно, существуют, например Фортран. Я не знаю языков, в которых используется явное «continue» для перехода к следующему случаю.
Я считаю, что это историческая причина из-за того, что такой случай может быть запрограммирован на «низком уровне». Более того, синтаксический аспект case - это метка, а break работает как в циклах, так что вы можете представить себе эквивалент, подобный этому:
if ( case == 1 ) goto lab1;
if ( case == 2 ) goto lab2;
if ( case == 3 ) goto lab3;
//...
default:
// default
goto switch_end;
lab1:
// do things
goto switch_end; // if break is present
lab2:
// do things, and follow to lab3
lab3:
// lab3 stuffs
goto switch_end;
//
...
switch_end: // past all labS.
В объектно-ориентированных языках используется шаблон Цепочка ответственности . То, как это реализовано, может варьироваться. В своем примере вы описываете смешивание конечного автомата с поведением, злоупотребляя оператором switch. Для вашего конкретного примера подходящей реализацией будет шаблон цепочки ответственности с параметром, который цепочка оценивает как шаблон Состояние , который видоизменяется по мере продвижения вниз по цепочке.
Хотя это не совсем то, о чем вы просили, в Groovy есть очень мощный оператор switch
OP говорит о «провале», но меня это очень редко когда-либо укусило.
Тем не менее, много раз я был поражен нерасширяемыми проектами. А именно, утверждения «switch (kbHit)» с несколькими сотнями ключей внутри - кошмар обслуживания, частое место для «божественных методов» и гигантских груд спагетти-кода.
Использование переключателя часто является признаком плохого объектно-ориентированного программирования. Как ответил другой человек, «2 использования Switch в 48 исходных файлах» в одном из его приложений показывают программиста, который не слишком полагается на эту конструкцию. Судя по его метрике, я предполагаю, что он, вероятно, по крайней мере, хороший структурированный программист и, вероятно, также понимает ООП / ООД.
Программисты ООП (не обязательно только на C ++) и даже чистые пользователи C, которым не навязана техника описания объектов, могут реализовать контейнер «инверсии управления», который публикует «нажатие клавиши» и позволяет подписчикам подключите их обработчики для "кода клавиатуры x". Это может значительно упростить чтение вашего кода.
Ada не имеет провалов и требует, чтобы все значения обрабатывались явным образом или добавлялось предложение «другие» для обработки остальных.
Оператор SQL CASE также не дает сбоев. XSLT не проваливается.
Похоже, что это C и производные языки, которые потерпели неудачу. Это довольно коварно, и единственное реальное применение, которое я видел, - это реализация устройства duff .
Эй, не забудь ОЦЕНКА КОБОЛА:
EVALUATE MENU-INPUT
WHEN "0" PERFORM INIT-PROC
WHEN "1" THRU "9" PERFORM PROCESS-PROC
WHEN "R" PERFORM READ-PARMS
WHEN "X" PERFORM CLEANUP-PROC
WHEN OTHER PERFORM ERROR-PROC
END-EVALUATE.
Я думаю, что ответ на ваш вопрос о том, почему так происходит, кроется в двух поведении, оба из которых связаны со сгенерированным ассемблерным кодом из исходного текста на языке Си.
Первая заключается в том, что в ассемблере выполняется текущая инструкция, и если нет перехода или другой инструкции управления потоком, будет выполнена инструкция по следующему адресу. Наивная компиляция оператора switch на ассемблере привела бы к появлению кода, который просто начал бы выполнять первую инструкцию, которая должна была бы проверить, есть ли соответствующее условие...
Второй связанной причиной является понятие таблицы ветвлений или списка переходов. По сути, компилятор может взять то, что он знает о вашем значении, и создать чрезвычайно эффективный машинный код для того же самого. Возьмем, к примеру, такую простую функцию, как atoi, которая преобразует строковое представление числа и возвращает его в целочисленной форме. Упростив все до поддержки только одной цифры, можно написать код, подобный этому:
int atoi(char c) {
switch (c) {
case '0': return 0;
case '1': return 1;
// ....
}
}
Наивный компилятор, возможно, просто преобразует это в серию блоков if/then, что означает, что для числа 9 потребуется значительное количество тактов процессора, в то время как 0 возвращается почти сразу. Используя таблицу ветвлений, компилятор мог бы выдать некоторый [псевдо] ассемблер, который бы немедленно "перепрыгнул" к правильному предложению возврата:
0x1000 # stick value of c in a register
0x1004 # jump to address c + calculated offset
# example '0' would be 0x30, the offset in for this sample
# would always be 0x0FD8... thus 0x30 + 0x0FD8 = 0x1008
0x1008 # return 0
Извиняюсь: мои навыки в ассемблере и Си довольно ржавые. Надеюсь, это поможет прояснить ситуацию. 0x
Вот ответ: http://en.wikipedia.org/wiki/Switch_statement
Он называется оператором провала ( continue
в примере) и существует на следующих языках:
Go, Perl, C #
В C # он не будет компилироваться без оператора break
или goto case
(кроме случаев, когда нет предварительного действия).
Python не имеет его вообще .
Потребовалось некоторое время, чтобы привыкнуть, но у меня есть некоторые ужасающие воспоминания, связанные с массивными блокировками switch
, когда я еще работал с C #. Я без него намного счастливее.
А VB .NET обрабатывает это немного более похоже на то, как вы ожидаете, что это должно работать.
Select Case i
Case 1 to 3
DoStuff(i)
Case 4,5,6
DoStuffDifferently(i)
Case Is >= 7
DoStuffDifferentlyRedux(i)
Case Else
DoStuffNegativeNumberOrZero(i)
End Select
Здесь вообще нет никакого падения, без возможного использования Goto
Scala pattern matching я считаю огромным улучшением в этих случаях. :)
object MatchTest2 extends Application {
def matchTest(x: Any): Any = x match {
case 1 => "one"
case "two" => 2
case y: Int => "scala.Int"
}
println(matchTest("two"))
}
Пример с scala-lang.org
Чистая спекуляция, но:
Я иногда пишу C или Java, в которых говорю что-то вроде:
switch (tranCode)
{
case 'A':
case 'D':
case 'R':
processCredit();
break;
case 'B':
case 'G':
processDebit();
break;
default:
processSpecial();
}
То есть я намеренно использую fall-thru, чтобы позволить нескольким значениям запускать одну и ту же операцию.
Интересно, если это то, о чем думали изобретатели C, когда они создавали заявление SWITCH, что это было бы нормальным использованием.