Должны ли операторы switch всегда содержать предложение по умолчанию?

в одном из моих первых обзоров кода (некоторое время назад) мне сказали, что рекомендуется включать предложение по умолчанию во все операторы switch. Недавно я вспомнил этот совет, но не могу вспомнить, каким было оправдание. Сейчас это звучит довольно странно.

  1. есть ли разумная причина всегда включать оператор по умолчанию?

  2. зависит ли этот язык? Я не помню, какой язык я использовал в то время - может быть, это относится к некоторым языкам и не относится к другим?

19 ответов


переключить случаи должны почти всегда default случае.

причины использовать default

1.Чтобы "поймать" неожиданное значение

switch(type)
{
    case 1:
        //something
    case 2:
        //something else
    default:
        // unknown type! based on the language,
        // there should probably be some error-handling
        // here, maybe an exception
}

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

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

3. Чтобы показать кому-то, кто читает ваш код, что вы охватили это дело.

variable = (variable == "value") ? 1 : 2;
switch(variable)
{
    case 1:
        // something
    case 2:
        // something else
    default:
        // will NOT execute because of the line preceding the switch.
}

это был упрощенный пример, но дело в том, что кто-то, читающий код, не должен удивляться, почему variable не может быть чем-то, кроме 1 или 2.


единственный случай, который я могу придумать, чтобы не использовать default is когда переключатель проверяет что-то, где его довольно очевидно, что любая другая альтернатива может быть счастливо проигнорирована

switch(keystroke)
{
    case 'w':
        // move up
    case 'a':
        // move left
    case 's':
        // move down
    case 'd':
        // move right
    // no default really required here
}

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

все может пойти не так. Ценности не будут такими, как вы ожидаете, и так далее.

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

вот почему вы всегда должны использовать предложение по умолчанию и выдавать ошибку, например, в Java:

switch (myVar) {
   case 1: ......; break;
   case 2: ......; break;
   default: throw new RuntimeException("unreachable");
}

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


нет.

Что делать, если нет действия по умолчанию, контекст важен. Что, если вы хотите действовать только по нескольким ценностям?

берите пример чтения нажатий клавиш для игры

switch(a)
{
   case 'w':
     // Move Up
     break;
   case 's':
     // Move Down
     break;
   case 'a':
     // Move Left
     break;
   case 'd':
     // Move Right
     break;
}

добавляем:

default: // Do nothing

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


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

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

enum SomeEnum
{
    ENUM_1,
    ENUM_2,
    // More ENUM values may be added in future
};

int foo(SomeEnum value)
{
    switch (value)
    {
    case ENUM_1:
        return 1;
    case ENUM_2:
        return 2;
    }
    // handle invalid values here
    return 0;
 }

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

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


должен ли оператор "switch"всегда включить пункт по умолчанию? Нет. Он должен!--3-->обычно включить по умолчанию.

включение предложения по умолчанию имеет смысл только в том случае, если для него есть что делать, например, утверждать условие ошибки или предоставлять поведение по умолчанию. В том числе одно "просто потому что"-это карго-культовое программирование и не дает никакой ценности. Это" переключатель", эквивалентный тому, что все операторы" if "должны включать"else".

здесь тривиальный пример того, где это не имеет смысла:

void PrintSign(int i)
{
    switch (Math.Sign(i))
    {
    case 1:
        Console.Write("positive ");
        break;
    case -1:
        Console.Write("negative ");
        break;
    default: // useless
    }
    Console.Write("integer");
}

Это эквивалентно:

void PrintSign(int i)
{
    int sgn = Math.Sign(i);
    if (sgn == 1)
        Console.Write("positive ");
    else if (sgn == -1)
        Console.Write("negative ");
    else // also useless
    {
    }
    Console.Write("integer");
}

насколько я вижу, ответ "default" является необязательным, говоря, что коммутатор всегда должен содержать значение по умолчанию, это как сказать, что каждый "if-elseif" должен содержать "else". Если есть логика, которая должна быть выполнена по умолчанию, то оператор "default" должен быть там, но в противном случае код может продолжать выполняться без каких-либо действий.


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


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

поэтому я считаю, что если значение по умолчанию не должно быть достигнуто - вам не нужно его добавлять.

обратите внимание, что "не должен be reached " означает, что если он достиг, это ошибка в программном обеспечении - вам нужно проверить значения, которые могут содержать нежелательные значения из-за ввода пользователем и т. д.


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

switch(ResponseValue)
{
    default:
    case No:
        return false;
    case Yes;
        return true;
}

по крайней мере, это не обязательно в Java. Согласно JLS, он говорит, что atmost может присутствовать один случай по умолчанию. Это означает, что ни один случай по умолчанию не приемлем . Иногда это также зависит от контекста, который вы используете оператор switch. Например, в Java следующий блок коммутатора не требует значения по умолчанию

private static void switch1(String name) {
    switch (name) {
    case "Monday":
        System.out.println("Monday");
        break;
    case "Tuesday":
        System.out.println("Tuesday");
        break;
    }
}

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

    private static String switch2(String name) {
    switch (name) {
    case "Monday":
        System.out.println("Monday");
        return name;

    case "Tuesday":
        System.out.println("Tuesday");
        return name;

    default:
        return name;
    }
}

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


вы должны иметь значение по умолчанию, чтобы поймать неожиданные значения.

однако я не согласен с Адрианом Смитом, что ваше сообщение об ошибке по умолчанию должно быть чем-то совершенно бессмысленным. Может быть не обработанный случай, который вы не предвидели (что является своего рода точкой), что ваш пользователь в конечном итоге увидит, и сообщение типа "недостижимый" совершенно бессмысленно и не помогает никому в этой ситуации.

дело в том, сколько раз у вас было совершенно бессмысленный BSOD? Или неустранимая @ 0x352FBB3C32342?


это необязательное кодирование "соглашение". В зависимости от пользы ли или не она необходима. Я лично считаю, что если вам это не нужно, его не должно быть там. Зачем включать то, что не будет использоваться или пользователем?

Если возможности случая ограничены (т. е. логическое), то предложение по умолчанию резервные!


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

switch ( x ){
  case 0 : { - - - -}
  case 1 : { - - - -}
}

/* What happens if case 2 arises and there is a pointer
* initialization to be made in the cases . In such a case ,
* we can end up with a NULL dereference */

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

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


, потому что MISRA C говорит так:

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

тем не менее, я рекомендую не следовать MISRA C на этом, для большинства программ:

  • этой защитное Программирование гид по стилю не важно это только некоторые значения может быть допустимым – если переменная физически способна принимать значение, даже если это будет ошибка, вы должны ее обрабатывать. Большинство программ предпочитают печатать трассировку стека вместо "обработки" ошибок (как упоминалось Офиром Йоктаном).
  • Enum switches, в частности, не должны иметь предложения по умолчанию (как сказал Харлан Касслер). И, как также наглядно демонстрирует Харлан, обработка недопустимых значений может быть выполнена вне коммутатора-точка, которая отсутствовала в Мишра это обсуждение.

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


Если значение Switch (переключатель(переменная)) не может достичь случая по умолчанию, тогда случай по умолчанию вообще не нужен. Даже если мы сохраняем случай по умолчанию, он вообще не выполняется. Это мертвый код.


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


должны ли операторы switch всегда содержать предложение по умолчанию ? Никакие случаи переключателя не могут существовать без случая по умолчанию, в случае переключателя случай по умолчанию вызовет значение переключателя switch(x) в этом случае x, когда не совпадают с любыми другими значениями case.