switchステートメントには常にデフォルト句を含める必要がありますか?


255

私が最初に行ったコードレビューの1つ(しばらく前)で、すべてのswitchステートメントにdefault句を含めることをお勧めします。私は最近このアドバイスを思い出しましたが、正当化が何であったか思い出せません。今の私にはかなり奇妙に聞こえます。

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

  2. この言語に依存していますか?当時使用していた言語を覚えていません-これは一部の言語に適用され、他の言語には適用されませんか?


9
言語に大きく依存します
skaffman

回答:


277

スイッチケースには、ほとんど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.
}

これは過度に単純化された例ですが、要点は、コードを読んでいる人は、なぜvariable1または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
}

26
キーストロークに関するあなたの例は、あなたが今言った残りの部分と矛盾しています。:| 私はもう少し説明が理にかなっていると思います:この場合、デフォルトは気にしません。別のキーが押されても気にしないので、渡された変数が予想と異なる場合は気にします。
Andrew

1
同時に、GETパラメーターをチェックしている場合、ユーザーが取得URLを無効なものに変更するたびに例外がスローされることは望ましくない可能性があります:[
Andrew

4
@Andrew WASDは、コンピューターゲームのキャラクターの動きに共通です。他のキーを他の操作に割り当てることができるため、デフォルトのアクションは必要ありません。この種のスイッチでは、移動機能を呼び出すと便利です。
jemiloii 2016

13
一部のコンパイラは、ケースが見つからない場合に列挙型をオンにすると警告し、デフォルトのケースを追加するとその警告が抑制されます。これが私の経験では非常に一般的なエラーの原因であることを考えると(列挙値が追加された後にどこかでスイッチを更新し忘れている)、これはデフォルトのケースを省略する非常に良い理由のようです。
rdb

3
@virusrocks条件が列挙型の場合でも、予期しない値が残る可能性があります。たとえば、新しい値を列挙型に追加し、スイッチの更新を忘れた場合などです。また、一部の言語では、整数値を列挙型に割り当てることができます。C ++ enum MyEnum { FOO = 0, BAR = 1 }; MyEnum value = (MyEnum)2;では、またはMyEnumと等しくない有効なインスタンスを作成します。これらの問題のいずれかがプロジェクトでエラーを引き起こす場合、デフォルトのケースは、多くの場合デバッガーを必要とせずに、非常に迅速にエラーを見つけるのに役立ちます!FOOBAR
cdgraham

54

番号。

デフォルトのアクションがない場合、コンテキストは重要です。いくつかの値のみに基づいて行動したい場合はどうなりますか?

ゲームのキープレスを読む例をとる

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

時間の無駄であり、理由もなくコードの複雑さが増します。


14
良い例え。しかし、実際には、単純な// do nothingコメントを含むデフォルト句を追加すると、すべてのケースがカバーされない場合でも「OK」であることが明らかになります。
ネイル

8
いいえ。コーディングは、いくつかのケースを処理するだけではありません。また、ドキュメントとしても機能します。default:を記述し、// //何もしないまたは何か他のことを行うようにコメントすることで、コードの可読性が向上します。
JongAm Park 2015

22
他のコメントに同意しない。デフォルトの追加//コードがどのように機能するかわからない場合を除き、何も追加しません。デフォルトなしでswitchステートメントを見ると、デフォルトでは何もしないことがわかります。乱雑さを追加しても、これはより明白にはなりません。
Robert Noack

丁度。これは、al post paramsの処理と同じ概念です。あなたは他の人ではなくあなたが気にするものを扱います。
ジミー・ケイン

2
@jybrd以前の開発者が意図的にデフォルトを省略したと思わないのであれば、なぜそれを入れるのが正しいと信じるのですか?デフォルトは、完全に欠落するのと同じくらい簡単に偶然に空になる可能性があります。些細なことにはコメントは必要ありません。これはコードの混乱です。代わりに完全なカバレッジユニットテストを記述して、意図を示します。(ちょうど私の意見)
ロバートノ

45

デフォルトのケースがないことは、実際には状況によっては有益な場合があります。

スイッチのケースが列挙値である場合、デフォルトのケースがないことで、ケースがない場合にコンパイラの警告が表示されることがあります。そうすれば、将来新しい列挙値が追加され、これらの値のケースをスイッチに追加し忘れた場合、コンパイル時に問題を見つけることができます。無効な値が列挙型にキャストされた場合に備えて、コードが未処理の値に対して適切なアクションを実行することを確認する必要があります。したがって、これは、ブレークするのではなく列挙型のケース内に戻ることができる単純なケースに最適です。

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

これは重要な観察です!intをenumにキャストできないため、Javaでさらに適用されます。
Lii

2
あなたの例では、switch-statementの後に戻る代わりに例外をスローすることができます。このようにして、コンパイラーによる静的チェックと予期しない事態が発生した場合の明確なエラーの両方を得ることができます。
Lii

1
同意する。例ごとのSwiftでは、スイッチは完全でなければなりません。そうしないと、コンパイラエラーが発生します。デフォルトを指定すると、この有用なエラーが表示されなくなります。一方、すべてのケースが処理される場合にデフォルトを指定すると、実際にはコンパイラ警告(到達不能ステートメント)がスローされます。
アンディ

1
バグを修正しただけで、デフォルトがない場合はコンパイラから警告が表示されていました。そのため、列挙型変数の切り替えにはデフォルトがないはずです。
notan 2018年

42

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

物事はうまくいかないことができます。値は期待したものとはなりません。

デフォルト句を含めたくない場合は、可能な値のセットを知っていると確信していることを意味します。可能性のある値のセットを知っていると確信している場合、値がこの可能性のある値のセットの外にある場合は、そのことを通知する必要があります。これは間違いなくエラーです。

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

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

「到達不能」文字列以外の情報を含める理由はありません。それが実際に発生した場合は、とにかくソースや変数の値などを確認する必要があり、例外スタックトレースにはその行番号が含まれるため、例外メッセージにテキストを書き込む時間を無駄にする必要はありません。


39
私は希望throw new RuntimeException("myVar invalid " + myVar);それはあなたにこれが再現するのは難しいですめったに発生しない問題がある場合は難しいかもしれ変数を、デバッガを使用して検査することなく、バグを把握し、修正するのに十分な情報を与える可能性があるため。
Chris Dodd

1
値がいずれかのケースと一致しないことがエラー条件でない場合はどうなりますか?
Gabe

4
エラー状態でない場合は、は必要ありませんdefault:。しかし、そうではないことが多いのですが、myVarリストされている以外の値は決して持てないと考えているため、デフォルトを省略しています。ただし、変数が「可能性がある」値以外の値を持っている場合、実際に驚いた回数を数えることはできません。これらの場合、後で他のエラー(デバッグがより困難)や間違った答え(テストで見落とされる可能性がある)を引き起こすのではなく、すぐにそれを確認できる例外に感謝しました。
エイドリアン・スミス

2
私はエイドリアンに同意します。懸命に失敗し、早期に失敗します。
Slappy

この状況は、処理された値の1つであると表明するアサーションに非常に似ているため、AssertionErrorではなくをスローすることを好みます。また、誰かがエラーをキャッチして飲み込む可能性は低くなります。RuntimeExceptionmyVar
Philipp Wendler、2011年

15

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

デフォルトのケースは必ずしも必要ではないが、常にそれを必要とすることで、コードアナライザーで簡単にチェックできることを説明できます。


1
仕事でも同じ要件があります。厳格な安全性チェックに合格する必要があり、多くの場合EMIの影響を受ける組み込みµControllerのコードを記述します。列挙された変数が列挙リストにない値を持つことは決してないと仮定するのは無責任です。
oosterwal 2011年

12

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

default句を含めることは、エラー条件をアサートしたり、デフォルトの動作を提供するなど、何かする必要がある場合にのみ意味があります。1つの「理由」を含めることはカーゴカルトプログラミングであり、価値はありません。これは、すべての「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");
}

同意しません。私見、デフォルトが存在してはならない唯一の時は、今のところ、入力のセットを変更することができず、すべての可能な値をカバーする方法がない場合です。私が考えることができる最も単純なケースは、データベースからのブール値であり、唯一の答えは(SQLが変更されるまで)true、false、およびNULLです。それ以外の場合は、「Should n't Happen!」のアサーションまたは例外を持つデフォルトがあります。理にかなっています。コードが変更され、1つ以上の新しい値がある場合は、それらのコードを確実に作成できます。そうしないと、テストが失敗します。
Harper Shelby

4
@ハーパー:あなたの例は「エラー状態のアサート」のカテゴリーに該当します。
Gabe、2011年

私の例は標準であり、可能性のあるすべてのケースが100%確実であり、デフォルトが不要である少数のケースは例外です。あなたの答えは、(少なくとも私にとっては)何もしないデフォルトが頻繁に発生するように聞こえるように表現されています。
Harper Shelby

@ハーパー:OK、私は何もしない状況があまり一般的でないことを示すために文言を変更しました。
Gabe

4
default:これらの場合のa はあなたの仮定を成文化したと思います。たとえば、sgn==0印刷するタイミングinteger(ポジティブでもネガティブでもない)は問題ありませんか、それともエラーですか?そのコードを読んでいる私にとって、言うのは難しいです。私はあなたzeroがその場合には書いたくないと思いますinteger、そしてプログラマーはsgn-1または+1だけであることができるという仮定をしたと思います。その場合、default:プログラマーは仮定エラーを早期にキャッチしてコードを変更することができます。
エイドリアン・スミス

7

私が見る限り、「デフォルト」はオプションです。スイッチには常にデフォルトが含まれている必要があると言うのは、すべての「if-elseif」に「else」が含まれている必要があると言うのと同じです。デフォルトで実行されるロジックがある場合、「デフォルト」ステートメントが存在するはずですが、それ以外の場合、コードは何もせずに実行を継続できます。


6

本当に必要でないときにデフォルトの句を持つことは防御的なプログラミングです これは通常、エラー処理コードが多すぎるためにコードが過度に複雑になります。このエラー処理および検出コードは、コードの可読性を損ない、メンテナンスを困難にし、最終的には解決するよりも多くのバグにつながります。

したがって、デフォルトに到達しない場合は、追加する必要はないと私は信じています。

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


3
予期しないケースが発生するもう1つの一般的な理由:コードの他の部分が、おそらく数年後に変更されます。
Hendrik Brummermann、2011年

実際、これは非常に危険な動作です。少なくとも、assert()句を追加します。その後、バグがあるたびに簡単に検出されます。
カートパティン2013

5

言語に依存すると思いますが、Cで列挙型をオンにしてすべての可能な値を処理する場合は、デフォルトのケースを含めない方がよいでしょう。このようにして、後でenumタグを追加し、それをスイッチに追加するのを忘れた場合、有能なコンパイラーにより、欠落しているケースに関する警告が表示されます。


5
「すべての可能な値」が列挙型で処理されることはほとんどありません。特定の値が明示的に定義されていなくても、整数を列挙型にキャストできます。そして、どのコンパイラが行方不明のケースについて警告しますか?
TrueWill

1
@TrueWill:はい、明示的なキャストを使用して、理解できない難読化されたコードを記述できます。理解しやすいコードを書くには、それを避けるべきです。 gcc -Wall(有能なコンパイラの最も一般的な分母の一種)は、switchステートメントで処理されない列挙型に関する警告を表示します。
クリス・ドッド

4

switchステートメントに厳密に定義されたラベルまたは値のセットのみが含まれることがわかっている場合は、これをベースをカバーするように実行するだけで、常に有効な結果が得られます。他の値の最適なハンドラーになります。

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

コロンはdefaultまだここで必要ですか?または、これを行うと、省略できる特別な構文が許可されますか?
Ponkadoodle 2013

コロンは必須です。これはタイプミスでした。注意を払っていただきありがとうございます。
2013

3

少なくともJavaでは必須ではありません。JLSによると、デフォルトのケースは最大で1つ存在する可能性があるとのことです。つまり、デフォルトのケースは受け入れられません。また、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ステートメントを指定するだけで、デフォルトのケースがなくても上記のメソッドのコンパイルエラーを回避できますが、デフォルトのケースを指定すると読みやすくなります。


3

MISRA Cなど、一部の(古い)ガイドラインにはそう記載されています。

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

そのアドバイスは現在関連する基準に基づいていないため、古くなっています。明白な省略は、ハーランカスラーが言ったことです:

デフォルトのケースを除外すると、未処理のケースを検出したときにコンパイラーがオプションで警告または失敗するようになります。静的な検証可能性は、結局のところ、動的チェックよりも優れているため、動的チェックが必要な場合の犠牲にはなりません。

ハーランも示したように、デフォルトのケースと同等の機能を切り替え後に再作成できます。これは、各ケースが早期の復帰である場合は簡単です。

動的チェックの一般的なニーズは、広い意味での入力処理です。値がプログラムの制御外からのものである場合、その値は信頼できません。

これは、Misraが極端な防御プログラミングの立場をとる場所でもあります。これにより、無効な値が物理的に表現可能である限り、プログラムが正しいことが証明できるかどうかに関係なく、チェックする必要があります。ソフトウェアがハードウェアエラーの存在下で可能な限り信頼できる必要がある場合、これは理にかなっています。しかし、Ophir Yoktanが言ったように、ほとんどのソフトウェアはバグを「処理する」よりはましです。後者の方法は、不快なプログラミングと呼ばれることがあります


2

入ってくる予期しない値をキャッチするデフォルトが必要です。

ただし、デフォルトのエラーメッセージはまったく意味のないものである必要があるというエイドリアンスミス氏には同意しません。ユーザーが最終的には表示せず、「到達不能」のようなメッセージはまったく意味がなく、その状況では誰も助けにはならない、予期しなかった未処理のケースがあるかもしれません(これは一種のポイントです)。

例として、まったく意味のないBSODを何回経験しましたか?または、致命的な例外@ 0x352FBB3C32342?


例-エラーメッセージが常に表示されるのは開発者だけではないことに注意してください。私たちは現実の世界に住んでいます、人々は間違いを犯します。
ジョンハント

2

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


2

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

ケースの可能性が限られている場合(つまりブール値)、デフォルトの句は冗長です!


2

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

enumで使用されるスイッチでは、デフォルトのケースは必要ない場合があります。スイッチにすべての値が含まれている場合、デフォルトのケースは実行されません。したがって、この場合は必要ありません。


1

特定の言語のswitchの動作に依存しますが、ほとんどの言語では、大文字と小文字が一致しない場合、警告なしにswitchステートメントが実行されます。いくつかの値のセットを期待し、それらをスイッチで処理したが、入力で別の値を取得したとします。何も起こらず、何も起こらなかったことがわかります。デフォルトでケースをキャッチした場合、何か問題があったことがわかります。


1

上記のVanwarilの最も投票された回答に同意しません。

どんなコードでも複雑さが増します。また、テストと文書化を行う必要があります。したがって、より少ないコードを使用してプログラミングできれば、常に良いことです。私の意見では、網羅的ではないswitchステートメントにはdefault句を使用していますが、徹底的なswitchステートメントにはdefault句を使用していません。私がそうしたことを確実にするために、私は静的コード分析ツールを使用します。だから詳細に行きましょう:

  1. 完全ではないswitchステートメント:これらには常にデフォルト値が必要です。名前が示すように、これらはすべての可能な値を網羅していないステートメントです。これも不可能である可能性があります。たとえば、整数値または文字列のswitchステートメントなどです。ここでヴァンワリルの例を使用したいと思います(彼がこの例を使用して間違った提案をしたと思います。ここでそれを使用して反対を述べます->デフォルトのステートメントを使用します):

    switch(keystroke)
    {
      case 'w':
        // move up
      case 'a':
        // move left
      case 's':
        // move down
      case 'd':
        // move right
      default:          
        // cover all other values of the non-exhaustive switch statement
    }
    

    プレーヤーは他のキーを押すことができます。次に、何もできなかったか(これは、デフォルトのケースにコメントを追加するだけでコードに表示できます)、たとえば、画面に何かを印刷する必要があります。このケースは、発生する可能性があるため、関連しています。

  2. 網羅的なswitchステートメント:これらのswitchステートメントはすべての可能な値をカバーします。たとえば、グレードシステムタイプの列挙に関するswitchステートメントです。初めてコードを開発するとき、すべての値をカバーするのは簡単です。しかし、私たちは人間なので、いくつかを忘れる可能性はわずかです。さらに、後で列挙型の値を追加して、すべてのswitchステートメントを調整してそれらをすべて網羅するようにすると、エラーが発生する可能性があります。単純なソリューションは、静的コード分析ツールです。ツールは、すべてのスイッチステートメントをチェックし、それらが完全であるかどうか、またはデフォルト値があるかどうかをチェックする必要があります。以下は、徹底的なswitchステートメントの例です。最初に列挙型が必要です:

    public enum GradeSystemType {System1To6, SystemAToD, System0To100}
    

    次に、この列挙型の変数が必要GradeSystemType type = ...です。網羅的なswitchステートメントは次のようになります。

    switch(type)
    {
      case GradeSystemType.System1To6:
        // do something
      case GradeSystemType.SystemAToD:
        // do something
      case GradeSystemType.System0To100:
        // do something
    }
    

    したがってGradeSystemType、たとえばを拡張するとSystem1To3、静的コード分析ツールはデフォルト句がないことを検出し、switchステートメントが完全ではないため、保存されます。

もう1つだけ。常にdefault句を使用する場合、静的コード分析ツールは常にdefault句を検出するため、網羅的または非網羅的なswitchステートメントを検出できない場合があります。enumを別の値で拡張し、それを1つのswitchステートメントに追加するのを忘れた場合は通知されないため、これは非常に悪いです。


0

switchステートメントには常にデフォルト句を含める必要がありますか?デフォルトのケースがない場合、スイッチケースは存在できません。スイッチケースのデフォルトケースはswitch(x)、他のケース値と一致しない場合に、この場合xでスイッチ値をトリガーします。


0

これは非常に言語固有であり、C ++の場合は列挙クラス型のマイナーポイントだと思います。これは、従来の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;
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.