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

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

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

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

Ответ 1

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

Вы видите это LOT в управляемых меню программах и 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
}

Ответ 2

Нет.

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

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

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

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

Ответ 3

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

Вещи могут и идут не так. Значения не будут такими, какие вы ожидаете, и т.д.

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

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

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

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

Ответ 4

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

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

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;
 }

Ответ 5

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

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

Ответ 6

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

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

Ответ 7

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

Ответ 8

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

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

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

Ответ 9

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

Ответ 10

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

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

Ответ 11

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

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

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

    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 в конце, но предоставление стандартного кода делает его более читаемым.

Ответ 12

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

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

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

Ответ 13

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

Ответ 14

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

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

Ответ 15

Если в инструкции 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 */

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

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

Ответ 16

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

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

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

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

Ответ 17

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

Ответ 18

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

Ответ 19

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

Ответ 20

Я полагаю, что это довольно специфично для языка, а для случая C++ второстепенный момент для типа enum-класса. Который кажется более безопасным, чем традиционное перечисление. НО

Если вы посмотрите на реализацию std :: byte, это выглядит примерно так:

enum class byte : unsigned char {} ;

Источник: https://en.cppreference.com/w/cpp/language/enum

А также учтите это:

В противном случае, если T является типом перечисления, который либо ограничен областью действия, либо имеет область действия с фиксированным базовым типом, и если в braced-init-list есть только один инициализатор, и если преобразование из инициализатора в базовый тип не сужается, и если инициализация - это прямая инициализация списка, затем перечисление инициализируется с результатом преобразования инициализатора в его базовый тип.

(начиная с C++ 17)

Источник: https://en.cppreference.com/w/cpp/language/list_initialization

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

Однако мне действительно нравится то, что @Harlan Kassler сказал в своем посте, и я сам начну использовать эту стратегию в некоторых ситуациях.

Просто пример небезопасного класса enum:

enum class Numbers : unsigned
{
    One = 1u,
    Two = 2u
};

int main()
{
    Numbers zero{ 0u };
    return 0;
}