switchステートメント-到達できない場合のデフォルトのケースの処理


14

私のクラスが所有する列挙型の値を処理するためにswitchステートメントを使用していて、可能な値ごとにケースがある場合-「デフォルト」のケースを処理するコードを追加する価値はありますか?

enum MyEnum
{
    MyFoo,
    MyBar,
    MyBat
}

MyEnum myEnum = GetMyEnum();
switch (myEnum)
{
    case MyFoo:
        DoFoo();
        break;
    case MyBar:
        DoBar();
        break;
    case MyBat:
        DoBat();
        break;
    default:
        Log("Unexpected value");
        throw new ArgumentException() 
}

これは、このコードに到達できない(ユニットテストでも)ことが原因だとは思いません。私の同僚はこれに同意せず、これがMyEnumに追加される新しい値によって引き起こされる予期しない動作から私たちを保護すると考えています。

コミュニティとは何ですか?


MyEnumがnull不可型であるとしましょう。
sd

3
「今日」はnull不可です。明日はもうコードをメンテナンスしていません。または、「MyBiz」が列挙型に追加された場合はどうなりますか?メンテナンスに関するカレブのコメントは非常に密接です。

1
すべてのケースをカバーしないスイッチがある場合、致命的なエラーであることをコンパイラーに教えてください。

誰かが無効な値をキャストMyEnumしてスイッチに渡すとどうなりますか?
Mawgはモニカ回復言う

1
何語?Javaの場合、Enum内にメソッドを配置し、それを呼び出すだけで(ポリモーフィズム)、switchステートメントを完全に削除する必要があります。
user949300

回答:


34

デフォルトのケースを含めてもコードの動作は変わりませんが、コードのメンテナンス性が向上します。コードを明白な方法でブレークする(メッセージをログに記録し、例外をスローする)ことにより、来年の夏に会社が採用するインターンの大きな赤い矢印を追加して、いくつかの機能を追加します。矢印は、「ねえ、あなた!はい、あなたと話しています!列挙型に別の値を追加する場合は、ここにもケースを追加することをお勧めします。」その余分な努力により、コンパイルされたプログラムに数バイトが追加される可能性があります。しかし、1時間から1日の非生産的な頭の傷のどこか(おそらく将来のあなた)を救うことにもなります。


更新:上記の状況、つまり、後で列挙に追加された値に対する保護は、コンパイラーによってキャッチすることもできます。Clang(およびgcc、私は思う)は、列挙型をオンにしたが、列挙内のすべての可能な値をカバーするケースがない場合、デフォルトで警告を発行します。したがって、たとえば、defaultスイッチからケースを削除MyBazして列挙に新しい値を追加すると、次のような警告が表示されます。

Enumeration value 'MyBaz' not handled in switch

コンパイラに未発見のケースを検出させることは、defaultそもそもあなたの質問に影響を与えた到達不能なケースの必要性を大幅に排除することです。


2
わかりました、あなたは私を確信させました:)私はちょうど私のコードカバレッジ番号のへこみを受け入れなければならないでしょう。
sd

@stそのコードをテストできない理由はありません。列挙で追加の値を条件付きでコンパイルするテストビルドを実行し、それを使用する単体テストを作成します。おそらくそれは理想的ではありませんが、通常は到達しないコードをテストする必要があるのはおそらくそれだけではありません。
カレブ

または、列挙型以外の値を列挙型にキャストして使用します。
Mawgはモニカ回復言う

5

今朝も同僚と話をしていましたが、それは本当に残念なことですが、2つの理由から、安全のためにデフォルトを処理する必要があると思います。

まず、同僚が言及しているように、enumに追加される新しい値に対してコードを将来的に保証します。これは可能性のように思われるかもしれませんが、常にそこにあります。

さらに重要なことは、言語/コンパイラによっては、スイッチされた変数に列挙型のメンバーではない値を持つことができる場合があります。たとえば、C#の場合:

MyEnum myEnum = (MyEnum) 3; // This could come from anywhere, maybe parsed from text?
// ... code goes on for a while

switch ( myEnum )
{
    case MyEnum.A:
        // ... handle A case
        break;
    case MyEnum.B:
        // ... handle B case
        break;
}

// ... code that expects either A or B to have happened

単純なものcase default:を追加して例外をスローすることにより、「何も起こらないが「何か」が起こるはずだったこの奇妙なケースから身を守ることができました。

残念ながら、基本的にswitchステートメントを記述するたびに、列挙型のケースをチェックしているためです。「デフォルトでスロー」動作が言語自体によって強制されることを本当に願っています(少なくともキーワードを追加することによって)。


3

到達することを期待していなくても、デフォルトのケースを追加することは良いことです。コードがプログラムの後半ではなく、「これは起きてはならない」例外をすぐにスローし、ミステリアスな例外を投げたり、エラーなしで予期しない結果を返す場合、デバッグがはるかに容易になります。


2

私は言う:

に別のタイプを追加してみてくださいMyEnum。次に、この行を変更します。

MyEnum myEnum = GetMyEnum();

MyEnum myEnum = SomethingElse;

次に、デフォルトのケースを使用して、デフォルトのケースを使用せずにコードを実行します。あなたはどちらの行動が好きですか?

デフォルトケースを持つことはNULL値をトラップして防ぐのにも役立ちますNullPointerExceptions


-1

デフォルトのケースを主張することでマッハ時間をどのように節約できるか考えているなら、質問する必要はありません。エラー状態で静かに何もしないことは受け入れられません。決して発生しないはずの例外を静かにキャッチしますか?同様に受け入れられない、あなたに続くプログラマーのために「鉱山」を残します。

コードが変更または修正されることがなく、100%バグがない場合は、デフォルトのケースを除外しても問題ありません。

Ada(堅牢なプログラミング言語の祖父)は、すべての列挙型がカバーされているか、デフォルトのハンドラーがない限り、列挙型のスイッチをコンパイルしません。この機能は、すべての言語のウィッシュリストにあります。私たちのAdaコーディング標準では、enumのスイッチを使用して、すべての値を明示的に処理し、デフォルトなしでこの状況を処理するのが好ましい方法であると述べています。


なぜすべての-1なのか?
マッテンツ

2
私はあなたを-1しませんでしたが、あなたの態度のせいだと思います;)
フリーク
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.