Переключение оператора fallthrough в C#?
Switch заявление fallthrough является одним из моих личных основных причин для любви switch и if/else if конструктов. Пример в порядке здесь:
static string NumberToWords(int number)
{
string[] numbers = new string[]
{ "", "one", "two", "three", "four", "five",
"six", "seven", "eight", "nine" };
string[] tens = new string[]
{ "", "", "twenty", "thirty", "forty", "fifty",
"sixty", "seventy", "eighty", "ninety" };
string[] teens = new string[]
{ "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen",
"sixteen", "seventeen", "eighteen", "nineteen" };
string ans = "";
switch (number.ToString().Length)
{
case 3:
ans += string.Format("{0} hundred and ", numbers[number / 100]);
case 2:
int t = (number / 10) % 10;
if (t == 1)
{
ans += teens[number % 10];
break;
}
else if (t > 1)
ans += string.Format("{0}-", tens[t]);
case 1:
int o = number % 10;
ans += numbers[o];
break;
default:
throw new ArgumentException("number");
}
return ans;
}
умные люди съеживаются, потому что string[]s должны быть объявлены вне функции: ну, они есть, это просто пример.
компилятор завершается со следующей ошибкой:
Control cannot fall through from one case label ('case 3:') to another
Control cannot fall through from one case label ('case 2:') to another
почему? И есть ли способ получить такое поведение без трех ifs?
14 ответов:
(копирование / вставка ответ я дал в другом месте)
падения через
switch-cases может быть достигнуто, не имея кода вcase(см.case 0) или с помощью специальныхgoto case(см.case 1) илиgoto default(см.case 2) составляет:switch (/*...*/) { case 0: // shares the exact same code as case 1 case 1: // do something goto case 2; case 2: // do something else goto default; default: // do something entirely different break; }
"почему" -это избежать случайного провала, за что я благодарен. Это не редкий источник ошибок в C и Java.
обходной путь заключается в использовании goto, например
switch (number.ToString().Length) { case 3: ans += string.Format("{0} hundred and ", numbers[number / 100]); goto case 2; case 2: // Etc }общая конструкция переключателя / корпуса немного неудачна на мой взгляд. Он застрял слишком близко к C-есть некоторые полезные изменения, которые могут быть сделаны с точки зрения определения области и т. д. Возможно, более умный переключатель, который мог бы сделать сопоставление шаблонов и т. д., Был бы полезен, но это действительно меняется от переключения к" проверке последовательности условий " - в этот момент, возможно, будет вызвано другое имя.
Switch fallthrough исторически является одним из основных источников ошибок в современном программном обеспечении. Разработчик языка решил сделать обязательным переход в конце дела, если вы не выполняете по умолчанию следующее дело непосредственно без обработки.
switch(value) { case 1:// this is still legal case 2: }
чтобы добавить к ответам здесь, я думаю, что стоит рассмотреть противоположный вопрос в связи с этим, а именно. почему C допустил провал в первую очередь?
любой язык программирования, конечно, служит двум целям:
- предоставьте инструкции компьютеру.
- оставьте запись о намерениях программиста.
поэтому создание любого языка программирования-это баланс между тем, как лучше всего служить эти две цели. С одной стороны, чем проще превращаться в компьютерные инструкции (будь то машинный код, байт-код, подобный IL, или инструкции интерпретируются при выполнении), тем более эффективным, надежным и компактным будет процесс компиляции или интерпретации. Доведенная до крайности, эта цель приводит к тому, что мы просто пишем в сборке, IL или даже raw op-кодах, потому что самая простая компиляция-это там, где нет компиляции все.
и наоборот, чем больше язык выражает намерение программиста, а не средства, принятые для этой цели, тем более понятна программа как при написании, так и во время обслуживания.
теперь
switchвсегда можно было скомпилировать путем преобразования его в эквивалентную цепочкуif-elseблоки или аналогичные, но он был разработан как позволяющий компиляции в определенный общий шаблон сборки, где один принимает значение, вычисляет смещение от него (будь то поиск таблицы, индексированной идеальным хэшем значения, или фактической арифметикой по значению*). Стоит отметить, что сегодня компиляция C# иногда будет превращатьсяswitchв эквивалентеif-else, а иногда использовать хэш-основанный подход перехода (а также С C, C++ и другими языками с сопоставимым синтаксисом).в этом случае есть две веские причины для разрешения провала:
это просто происходит естественным образом во всяком случае: если вы создадите таблицу переходов в набор инструкций, а один из предыдущих пакетов инструкций не содержит какого-либо перехода или возврата, то выполнение просто естественным образом перейдет в следующий пакет. Разрешение провала было тем, что "просто произойдет", если вы повернете
switch-на C в прыжке-таблицы, используя машинный код.кодеры, которые писали в сборке, уже привыкли к эквиваленту: при написании таблицы переходов вручную в сборке они нужно было бы рассмотреть, будет ли данный блок кода заканчиваться возвращением, переходом за пределы таблицы или просто переходом к следующему блоку. Таким образом, имея кодер добавить явное
breakпри необходимости был "естественным" для кодера тоже.четыре десятилетия спустя, однако, все не совсем то же самое, по нескольким причинам:
- кодеры в C сегодня могут иметь мало или вообще не иметь опыта сборки. Кодеры во многих других языках C-стиля еще менее вероятны (особенно Javascript!). Любое понятие "к чему люди привыкли от собрания" уже не актуально.
- улучшения в оптимизации означают, что вероятность
switchлибо превращается вif-elseпотому что было сочтено, что подход, вероятно, будет наиболее эффективны или же превращены в особо эзотерический вариант прыжковые таблицы подхода выше. Сопоставление подходов более высокого и более низкого уровня уже не так сильно, как раньше.- опыт показал, что провал имеет тенденцию быть миноритарным случаем, а не нормой (исследование компилятора Sun обнаружило 3%
switchблоки использовали провал, отличный от нескольких меток на одном блоке, и считалось, что случай использования здесь означает, что эти 3% были в факт гораздо выше, чем обычно). Таким образом, изучаемый язык делает необычное более легко обслуживаемым, чем общее.- опыт показал, что провал имеет тенденцию быть источником проблем как в тех случаях, когда это случайно сделано, так и в тех случаях, когда правильное провал упущено кем-то, кто поддерживает код. Это последнее является тонким дополнением к ошибкам, связанным с падением, потому что даже если ваш код совершенно свободен от ошибок, ваше падение все еще может вызвать проблемы.
в связи с этими двумя последними пунктами рассмотрим следующую цитату из текущего издания K&R:
переход от одного случая к другому не является надежным, будучи склонным к распаду при изменении программы. За исключением нескольких меток для одного вычисления, провалы следует использовать экономно и комментировать.
в качестве вопроса хорошей формы, поставить перерыв после последнего случая (по умолчанию здесь), хотя это логически не нужно. Когда-нибудь, когда другой случай будет добавлен в конце, этот бит защитного программирования спасет вас.
так, из первых уст, что он проваливается в C-это проблематично. Считается хорошей практикой всегда документировать провалы с комментариями, что является применением общего принципа, согласно которому следует документировать, когда вы делаете что-то необычное, потому что это то, что приведет к более позднему изучению кода и/или сделает ваш код выглядит так, как будто в нем есть ошибка новичка, когда он на самом деле правильный.
и когда вы думаете об этом, такой код:
switch(x) { case 1: foo(); /* FALLTHRU */ case 2: bar(); break; }и добавление чего-то, чтобы сделать провал явным в коде, это просто не то, что может быть обнаружено (или чье отсутствие может быть обнаружено) компилятором.
таким образом, тот факт, что on должен быть явным с провалом в C# ,не добавляет никакого штрафа людям, которые хорошо писали в других Во всяком случае, языки C-стиля, поскольку они уже были бы явными в своих провалах.†
наконец, использование
gotoвот уже норма из C и других таких языков:switch(x) { case 0: case 1: case 2: foo(); goto below_six; case 3: bar(); goto below_six; case 4: baz(); /* FALLTHRU */ case 5: below_six: qux(); break; default: quux(); }в этом случае, когда мы хотим, чтобы блок был включен в код, выполняемый для значения, отличного от того, которое приводит к предыдущему блоку, тогда нам уже нужно использовать
goto. (Конечно, есть средства и способы избежать этого с различными но это верно практически для всего, что связано с этим вопросом). Как таковой C# построен на уже нормальном способе борьбы с одной ситуацией, когда мы хотим поразить более одного блока кода в Aswitch, и просто обобщил его, чтобы покрыть провал, а также. Это также сделало оба случая более удобными и самодокументированными, так как мы должны добавить новую метку в C, но можем использоватьcaseкак метка в C#. В C# мы можем избавиться отbelow_sixярлык и использоватьgoto case 5это яснее, к тому, что мы делаем. (Мы также должны добавитьbreakнаdefault, который я оставил только для того, чтобы сделать приведенный выше код C явно не C# код).таким образом, вкратце:
- C# больше не относится к неоптимизированному выходу компилятора так же непосредственно, как C-код 40 лет назад (и C в наши дни), что делает одно из вдохновений падения неуместным.
- C# остается совместимым с C, а не только с неявным
break, для облегчения обучения из языка теми, кто знаком с подобными языками, и проще портирование.- C# удаляет возможный источник ошибок или неправильно понятый код, который был хорошо документирован как вызывающий проблемы в течение последних четырех десятилетий.
- C# делает существующую передовую практику С C (провал документа) принудительной для компилятора.
- C# делает необычный случай с более явным кодом, обычный случай с кодом, который просто пишет автоматически.
- C# использует то же самое
goto-основанный подход для удара одного и того же блока из разныхcaseметки, как используется в C. Он просто обобщает его на некоторые другие случаи.- в C# делает это
goto-основанный подход более удобен и понятен, чем в C, позволяяcaseзаявления в качестве метки.в целом, довольно разумное дизайнерское решение
*некоторые формы BASIC позволит сделать такие, как
GOTO (x AND 7) * 50 + 240который в то же время хрупкий и, следовательно, особенно убедительный случай для запретаgoto, служит для того, чтобы показать эквивалент более высокого языка того способа, которым код нижнего уровня может сделать прыжок на основе арифметики на значение, что гораздо более разумно, когда это результат компиляции, а не то, что должно поддерживаться вручную. Реализации устройства Даффа, в частности, хорошо поддаются эквивалентному машинному коду или IL, потому что каждый блок инструкции часто будут одинаковой длины без необходимости добавленияnopфиллеры.†устройство Даффа снова появляется здесь, как разумное исключение. Тот факт, что с этим и подобными шаблонами есть повторение операций, служит для того, чтобы сделать использование провала относительно ясным даже без явного комментария к этому эффекту.
вы можете 'goto case label' http://www.blackwasp.co.uk/CSharpGoto.aspx
оператор goto-это простая команда, которая безоговорочно передает управление программой другому оператору. Команда часто критикуется с некоторыми разработчиками, выступающими за ее удаление со всех языков программирования высокого уровня, потому что это может привести к спагетти код. Это происходит, когда есть так много операторов goto или подобный скачок операторы, которые код становится трудно читать и поддерживать. Однако есть программисты, которые указывают, что оператор goto при тщательном использовании обеспечивает элегантное решение некоторых проблем...
они оставили это поведение, чтобы избежать, когда он не используется, но вызывает проблемы.
Он может использоваться только в том случае, если в части case нет оператора, например:
switch (whatever) { case 1: case 2: case 3: boo; break; }
Они изменили поведение оператора switch (из C/Java / C++) для c#. Я предполагаю, что причина заключалась в том, что люди забыли о падении, и были вызваны ошибки. Одна книга, которую я прочитал, сказала использовать goto для имитации, но это не похоже на хорошее решение для меня.
после каждого случая заявление require перерыв или перейти оператор, даже если это случай по умолчанию.
оператор перехода, такой как разрыв требуется после каждого блока case, включая последний блок ли это оператор case или значение по умолчанию заявление. За одним исключением, (в отличие от в C++ оператор switch), C# не поддержите неявное падение до конца от один случай метка к другому. Единица исключение - если оператор case имеет никакой код.
C# не поддерживает провал с помощью операторов switch/case. Не знаю, почему, но там действительно нет поддержки. связь
вы можете достичь падения, как c++ по ключевому слову goto.
EX:
switch(num) { case 1: goto case 3; case 2: goto case 3; case 3: //do something break; case 4: //do something else break; case default: break; }
просто быстрая заметка, чтобы добавить, что компилятор для Xamarin на самом деле получил это неправильно, и это позволяет fallthrough. Он якобы был исправлен, но не был выпущен. Обнаружил это в каком-то коде, который на самом деле проваливался, и компилятор не жаловался.
переключатель (ссылка C#) говорит
C# требует окончания секций коммутатора, включая последнюю,
Так что вам также нужно добавить
break;наdefaultраздел, в противном случае все равно будет ошибка компилятора.
вы забыли добавить оператор "break;" в случае 3. В случае 2 вы записали его в блок if. Поэтому попробуйте это:
case 3: { ans += string.Format("{0} hundred and ", numbers[number / 100]); break; } case 2: { int t = (number / 10) % 10; if (t == 1) { ans += teens[number % 10]; } else if (t > 1) { ans += string.Format("{0}-", tens[t]); } break; } case 1: { int o = number % 10; ans += numbers[o]; break; } default: { throw new ArgumentException("number"); }
Comments