not - switch case c# пример




Выключить инструкцию в C#? (10)

Вывод инструкции switch является одной из моих личных основных причин для любящего switch vs. if/else if constructs. Пример приведен в следующем порядке:

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

Зачем? И есть ли способ получить такое поведение без трех, if ?


Операция перехода, такая как разрыв, требуется после каждого блока case, включая последний блок, является ли это аргументом case или инструкцией по умолчанию. За одним исключением (в отличие от оператора C ++ switch), C # не поддерживает неявное прохождение от одного ярлыка к другому. Единственное исключение - если в case-case нет кода.

- C # switch () документация


«Почему» - это избежать случайного провала, за что я благодарен. Это не редкий источник ошибок в 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 - есть некоторые полезные изменения, которые могут быть сделаны с точки зрения охвата и т. Д. Возможно, более разумный переключатель, который мог бы выполнять сопоставление образцов и т. Д., Был бы полезен, но это действительно меняется от перехода к «проверке последовательности условий», - в этот момент, возможно, потребуется другое имя.


C # не поддерживает падение с помощью операторов switch / case. Не знаю, почему, но на самом деле нет никакой поддержки. Linkage


Вы забыли добавить «перерыв»; утверждение в случай 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");
}

Вы можете достичь падения через 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;
}

Они изменили инструкцию switch (из C / Java / C ++) для c #. Я предполагаю, что рассуждение состояло в том, что люди забыли о провале, и были вызваны ошибки. Одна книга, которую я читал, сказала, чтобы использовать goto для имитации, но это не похоже на хорошее решение для меня.


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

switch(value)
{
    case 1:// this is still legal
    case 2:
}

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


Чтобы добавить к ответам здесь, я думаю, что стоит рассмотреть противоположный вопрос в связи с этим, а именно: почему C разрешить провал в первую очередь?

Любой язык программирования, конечно, служит двум целям:

  1. Предоставьте инструкции компьютеру.
  2. Оставьте запись о намерениях программиста.

Таким образом, создание любого языка программирования является балансом между тем, как наилучшим образом служить этим двум целям. С одной стороны, проще всего превратиться в компьютерные инструкции (будь то машинный код, байт-код, например, IL, или инструкции интерпретируются при исполнении), тогда более возможно, что процесс компиляции или интерпретации будет эффективным, надежным и компактный выход. Достигнутая до предела, эта цель приводит к простому написанию в сборке, IL или даже необработанных op-кодах, потому что самая простая компиляция - это то, где компиляции вообще нет.

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

Теперь switch всегда может быть скомпилирован путем преобразования его в эквивалентную цепочку блоков if-else или аналогичных, но он был разработан как позволяющий компиляцию в конкретный общий шаблон сборки, где он принимает значение, вычисляет смещение от него (по поиск таблицы, индексированной совершенным хешем значения, или фактической арифметикой по значению *). Стоит отметить, что в настоящее время компиляция C # иногда превращается в эквивалент if-else , а иногда использует подход перехода на основе хэша (а также с C, C ++ и другими языками с сопоставимым синтаксисом).

В этом случае есть две веские причины для разрешения провала:

  1. В любом случае это происходит естественным образом: если вы построите таблицу перехода в набор инструкций, а одна из ранних партий инструкций не содержит какого-либо скачка или возврата, то выполнение просто естественным образом перейдет в следующую партию. Разрешить провал - это то, что «просто произойдет», если вы включили switch используя C в машинный код с использованием таблицы перехода.

  2. Кодеры, которые писали в сборке, уже были использованы для эквивалента: при написании таблицы переходов вручную в сборке они должны были бы рассмотреть, будет ли данный блок кода заканчиваться возвратом, прыжком за пределами таблицы или просто продолжать к следующему блоку. Таким образом, наличие кодера добавляет явный break когда это необходимо, «естественно» для кодера.

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

Четыре десятилетия спустя, однако, все не так, по нескольким причинам:

  1. Coders in C сегодня могут иметь мало или вообще не иметь опыта сборки. Кодеры во многих других языках C-стиля еще менее вероятны (особенно Javascript!). Любая концепция «того, что люди привыкли со сборкой» больше не имеет значения.
  2. Усовершенствования в оптимизации означают, что вероятность switch либо превращается в if-else потому что считается, что подход, который может быть наиболее эффективным, или же превратился в особенно эзотерический вариант подхода с прыжком-таблицей, выше. Сопоставление между подходами более высокого и низкого уровня не так сильно, как когда-то было.
  3. Опыт показал, что провал обычно является случаем меньшинства, а не нормой (исследование компилятора Sun обнаружило, что 3% блоков switch использовали провал, отличный от нескольких меток на одном блоке, и считалось, что использование В данном случае это означает, что эти 3% на самом деле намного выше, чем обычно). Таким образом, язык, изученный, делает необычное более легко удовлетворяемое, чем общее.
  4. Опыт показывает, что спад, как правило, является источником проблем как в случаях, когда он случайно выполнен, так и в тех случаях, когда правильное падение пропущено кем-то, поддерживающим код. Последнее является тонким дополнением к ошибкам, связанным с провалом, потому что даже если ваш код абсолютно не содержит ошибок, ваш провал может все еще вызвать проблемы.

В связи с этими двумя последними пунктами рассмотрим следующую цитату из текущей версии 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 #, построенный на уже нормальном способе решения одной ситуации, когда мы хотим поразить более одного блока кода в switch и просто обобщили его, чтобы покрыть провал. Это также сделало оба случая более удобными и самодокументирующими, так как мы должны добавить новую метку в C, но можем использовать этот case как метку на C #. В C # мы можем избавиться от метки below_six и использовать goto case 5 который более below_six относительно того, что мы делаем. (Мы также должны были бы добавить break для default , который я оставил только для того, чтобы сделать вышеуказанный код C явно не кодом C #).

Таким образом:

  1. C # больше не относится к неоптимизированному выводу компилятора так же, как C-код, сделанный 40 лет назад (и не C в наши дни), что делает одно из вдохновений неудачным несоответствующим.
  2. C # остается совместимым с C не только с неявным break , для более простого изучения языка теми, кто знаком с подобными языками, и упрощения переноса.
  3. C # удаляет возможный источник ошибок или неправильно понимаемый код, который был хорошо документирован как вызывающий проблемы в течение последних четырех десятилетий.
  4. C # делает существующую передовую практику с C (провалом документа), подлежащим исполнению компилятором.
  5. C # делает необычный случай тем, у кого более явный код, обычный случай тот, с кодом, который просто пишет автоматически.
  6. C # использует один и тот же подход, основанный на goto для того, чтобы ударить один и тот же блок из разных меток case как это используется в C. Он просто обобщает его на некоторые другие случаи.
  7. C # делает этот подход на основе goto более удобным и понятным, чем на C, позволяя операторам case действовать как метки.

В целом, довольно разумное дизайнерское решение

* Некоторые формы BASIC позволили бы делать такие, как GOTO (x AND 7) * 50 + 240 который в то время как хрупкий и, следовательно, особенно убедительный случай для запрета goto , служит для отображения эквивалента на более высоком языке такого рода что код нижнего уровня может сделать скачок на основе арифметики по значению, что гораздо разумнее, если результат компиляции, а не что-то, что нужно поддерживать вручную. Реализации Duff's Device, в частности, хорошо подходят для эквивалентного машинного кода или IL, потому что каждый блок инструкций часто будет иметь одинаковую длину без необходимости добавления nop заполнителей.

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


switch (C # Reference) говорит

C # требует окончания секций коммутатора, включая конечный,

Поэтому вам также нужно добавить break; к вашему разделу по default , иначе будет ошибка компилятора.





switch-statement