Должны ли операторы switch всегда содержать предложение по умолчанию? - PullRequest
220 голосов
/ 10 января 2011

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

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

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

Ответы [ 20 ]

245 голосов
/ 09 марта 2011

Случаи переключения должны почти всегда иметь 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. Вы также можете увидеть это, когда переменная объявляется вне регистра переключателя, но не инициализируется, и каждый случай инициализирует ее чем-то другим. Здесь по умолчанию нужно также инициализировать его, чтобы код строки, который обращается к переменной, не вызывал ошибку.

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, это , когда коммутатор проверяет что-то, где его довольно очевидная любая альтернатива может быть счастливо проигнорирована

switch(keystroke)
{
    case 'w':
        // move up
    case 'a':
        // move left
    case 's':
        // move down
    case 'd':
        // move right
    // no default really required here
}
45 голосов
/ 11 марта 2011

Нет.

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

Возьмите пример чтения нажатий клавиш для игры

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

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

42 голосов
/ 10 января 2011

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

Все может пойти и пойти не так.Значения не будут соответствовать вашим ожиданиям и т. Д.

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

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

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

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

39 голосов
/ 19 июня 2015

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

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

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;
 }
13 голосов
/ 10 марта 2011

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

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

12 голосов
/ 10 января 2011

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

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

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

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");
}
7 голосов
/ 28 сентября 2012

Насколько я вижу, ответ «по умолчанию» является необязательным, говорить, что переключатель должен всегда содержать значение по умолчанию, все равно, что говорить, что каждое «if-elseif» должно содержать «другое». Если есть логика, которая должна быть сделана по умолчанию, тогда должен присутствовать оператор 'default', но в противном случае код мог бы продолжать выполняться, ничего не делая.

6 голосов
/ 09 марта 2011

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

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

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

5 голосов
/ 10 января 2011

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

4 голосов
/ 02 октября 2012

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

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