web-dev-qa-db-ja.com

switch文には必ずデフォルト句を含めるべきですか?

私の最初のコードレビュー(しばらく前)の中で、私はすべてのswitchステートメントにdefault節を含めるのは良い習慣だと言われました。私は最近このアドバイスを思い出しましたが、その正当性が何であったかを思い出すことができません。今はかなり変に聞こえます。

  1. 常にデフォルトのステートメントを含めることに賢明な理由はありますか?

  2. この言語は依存しますか?私がその時に使用していた言語を覚えていません - 多分これは他の言語ではなく一部の言語に当てはまりますか?

218
tttppp

スイッチケースはほとんど常に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の外側で宣言されていても初期化されていない場合や、各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を使用しないと考えることができる唯一のケースは、スイッチが何か他のすべての選択肢を喜んで無視できるものをチェックしているときです

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

いいえ.

デフォルトのアクションがない場合、コンテキストは重要です。いくつかの価値観に基づいて行動することだけを考えている場合はどうなりますか?

ゲームのキープレスを読む例を見てください

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

単なる時間の浪費であり、理由もなくコードの複雑さを増します。

45
Jared Kells

どの言語で作業していても、私は常にデフォルト句を使用します。

物事はうまくいかないし、うまくいかない。値はあなたが期待するものではないでしょう、など。

デフォルト句を含めたくないということは、可能な値のセットを知っているという自信があることを意味します。あなたがあなたが可能な値のセットを知っていると信じるならば、もしその値がこの可能な値のセットの外側にあるならば、あなたはそれについて知らされたいと思うでしょう - それは確かにエラーです。

これが、デフォルトの文節を常に使用してエラーをスローする必要がある理由です(例えば、Java)。

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

単なる "到達不能"文字列以上の情報を含める理由はありません。もしそれが実際に起こるのであれば、とにかくソースや変数の値などを調べる必要があるでしょう、そして例外スタックトレースはその行番号を含むので、例外メッセージにもっとテキストを書くのに時間を浪費する必要はありません。

42
Adrian Smith

デフォルトのケースを持たないことは、状況によっては実際には有益です。

あなたのスイッチケースがenums値であるならば、デフォルトケースを持たないことによって、あなたがケースを逃しているならば、あなたはコンパイラ警告を受けることができます。そうすれば、将来新しい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;
 }
38
Harlan Kassler

私の会社では、アビオニクスおよび防衛市場向けのソフトウェアを作成していますが、switchステートメント内のすべてのケースは明示的に処理する必要があるため、常にデフォルトステートメントを含めています。私たちは、予期せぬ(あるいは不可能と思われるものでさえも)値を不正に振る舞うか、単にクラッシュさせるだけの余裕はありません。

デフォルトのケースは常に必要というわけではありませんが、常にそれを必要とすることで、コードアナライザーによって簡単にチェックされます。

13
Kurt Pattyn

"switch"ステートメントには常にdefault句を含めるべきですか?いいえ。通常デフォルトを含める必要があります。

Default句を含めても意味があるのは、エラー条件を表明したりデフォルトの動作を提供したりするようなことが必要な場合だけです。 1つだけを含めても、それはカーゴカルトプログラミングであり、何の価値もありません。これは、すべての "if"ステートメントに "else"を含める必要があると言うことと同じ "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");
}
12
Gabe

私の知る限りでは、答えは「デフォルト」であり、スイッチに常にデフォルトを含める必要があると言うことは、すべての「if-elseif」に「else」を含まなければならないと言うのと同じです。デフォルトで実行するロジックがある場合は、 'default'ステートメントがあるはずですが、それ以外の場合、コードは何もせずに実行を継続できます。

7
Gershon Herczeg

デフォルトの節がない場合は本当に必要です Defensive programming これは通常、エラー処理コードが多すぎるためにコードが複雑になりすぎるためです。このエラー処理および検出コードはコードの可読性を損ない、メンテナンスをより困難にし、そして結局それが解決するより多くのバグをもたらします。

ですから、デフォルトに達してはいけないのであれば、追加する必要はありません。

「到達すべきではない」とは、それがソフトウェアのバグであることを意味します - ユーザー入力などのために不要な値を含む可能性がある値をテストする必要があるということです。

6
Ophir Yoktan

私はそれが言語に依存していると思うでしょうが、もしあなたがenum型をオンに切り替えていて、あなたが可能なすべての値を扱っているならば、あなたはおそらくデフォルトケースを含めないほうが良いでしょう。そうすれば、後で追加のenumタグを追加してそれをスイッチに追加するのを忘れた場合、有能なコンパイラーがケースの欠落について警告を発します。

5
Chris Dodd

あなたがswitch文がラベルや値の厳密に定義されたセットだけを持つことを知っているならば、ベースをカバーするためにこれをするだけで、あなたはいつも有効な結果を得るでしょう。他の値に対する最善のハンドラーになります。

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

少なくともJavaでは必須ではありません。 JLSによると、それはせいぜい1つのデフォルトケースが存在し得ると言います。これはデフォルトの大文字小文字の区別が許容されないことを意味します。ときには、switch文を使用しているコンテキストによっても異なります。たとえば、Javaでは、次のswitchブロックではデフォルトの大文字と小文字が区別されません。

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文を追加するだけで、デフォルトの大文字小文字を区別せずに上記のメソッドのコンパイルエラーを回避できますが、デフォルトの大文字と小文字を区別すると読みやすくなります。

3
Shiva Kumar

なぜなら MISRA C はそう言う:

最後のデフォルト節の要件は防御的プログラミングです。この条項は、適切な措置を講じるか、または何の措置も取らない理由について適切なコメントを含めるものとします。

そうは言っても、私はほとんどのソフトウェアについて、これをMISRA Cに従うことに反対することをお勧めします。

  • この防御的なプログラミングスタイルガイド 気にしない いくつかの値だけが有効であるかもしれない - 変数が物理的に取ることができるなら値に関しては、たとえそれがバグであったとしても、あなたはそれを処理することになっています。ほとんどのソフトウェアは、(Ophir Yoktanによって言及されているように)バグを「処理する」代わりにスタックトレースを印刷することを好むべきです。
  • 特にEnumスイッチには、(Harlan Kasslerが言っているように)デフォルトの句はありません。そして、Harlanもまた明らかに示しているように、無効な値を扱うことはスイッチの外で行うことができます。これは Misraの議論 には欠けていた点です。
2
user2394284

スイッチ値(switch(variable))がデフォルトのケースに到達できない場合は、デフォルトのケースはまったく必要ありません。デフォルトの場合をそのままにしても、まったく実行されません。デッドコードです。

2
shiju

これはオプションのコーディング規約です。用途によっては必要かどうかです。私は個人的には、あなたがそれを必要としないのであればそれはそこにあってはならないと信じています。ユーザーが使用または使用できないものを含めるのはなぜですか?

ケースの可能性が限られている(すなわちブール値)場合、デフォルトの句はredundant!です。

2
Danny Mahoney

予期しない値が入力されるのを防ぐために、デフォルトを設定する必要があります。

しかし、私はAdrian Smithに同意しません。デフォルトのエラーメッセージはまったく意味がないはずです。あなたがユーザーに会うことになってしまい、 "到達不能"のようなメッセージが全く無意味になり、そのような状況で誰も助けにならないというあなたが見逃した未処理のケース(ポイントの一種です)があるかもしれません。

その好例として、まったく意味のないBSODが何回あったことがありますか。または致命的な例外@ 0x352FBB3C32342?

2
John Hunt

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のケースが発生すると想定され、このケースで初期化しないと、nullポインタ例外で起動する可能性があります。それ故に、たとえそれが些細であるとしても、default caseステートメントを使用することが推奨されます。

2
Nik

特定の言語のスイッチがどのように機能するかに依存しますが、大部分の言語で大文字と小文字の区別がない場合、実行は警告なしにswitchステートメントを実行します。ある値のセットを期待してswitchでそれらを処理したと想像してみてください、しかしあなたは入力で別の値を得ます。何も起こらないし、何も起こらないことをあなたは知らない。あなたがデフォルトで事件を捉えた場合、あなたは何か問題があったことを知るでしょう。

1

Enumが使用するスイッチでは、デフォルトの大文字小文字の区別は必要ないかもしれません。 switchにすべての値が含まれていると、デフォルトのcaseは実行されません。したがって、この場合は必要ありません。

1
iOkay

これは言語固有のものであり、C++の場合、enum classタイプの小さなポイントだと思います。従来のC列挙型よりも安全に見えます。しかし

Std :: byteの実装を見ると、次のようなものです。

enum class byte : unsigned char {} ;

ソース: https://en.cppreference.com/w/cpp/language/enum

また、これも考慮してください。

それ以外の場合、Tが基底の固定型で範囲指定またはスコープ指定されていない列挙型であり、braced-init-listに初期化子が1つしかない場合、および初期化子から基底型への変換が縮小されない場合、および初期化は直接リスト初期化であり、初期化子を基礎型に変換した結果で列挙が初期化されます。

(C++ 17以降)

ソース: https://en.cppreference.com/w/cpp/language/list_initialization

これは、列挙子として定義されていない値を表す列挙クラスの例です。このため、列挙型を完全に信頼することはできません。アプリケーションによっては、これが重要になる場合があります。

しかし、@ Harlan Kasslerが彼の投稿で言ったことを本当に気に入っており、自分でいくつかの状況でその戦略を使い始めます。

安全でない列挙型クラスの例:

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

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

Switch文には必ずデフォルト句を含めるべきですか?デフォルトケース以外のスイッチケースは存在できません。スイッチケースのデフォルトケースでは、他のケース値と一致しない場合にスイッチ値switch(x)がトリガされます。

0
Nisal Edu