web-dev-qa-db-ja.com

Switchステートメントのフォールスルー...許可する必要がありますか?

覚えている限り、switchステートメントのフォールスルーの使用を避けてきました。実際、スイッチステートメントのバグにすぎないことを頭に深く掘り下げたため、物事を行うための可能な方法として意識に入ったことを思い出せません。しかし、今日、設計によってそれを使用するコードに出くわし、コミュニティの誰もがswitchステートメントのフォールスルーについてどう考えているのかすぐに疑問に思いました。

プログラミング言語が明示的に許可してはならないもの(回避策を提供しているもののC#が許可しているもの)か、プログラマーの手に渡るほど強力な言語の機能ですか?

編集:私は、フォールスルーが意味することに対して十分に具体的ではありませんでした。私はこのタイプをよく使います:

    switch(m_loadAnimSubCt){
        case 0: 
        case 1: 
            // Do something
            break;
        case 2:
        case 3:
        case 4:
            // Do something
            break;
   }

しかし、私はこのようなことを心配しています。

   switch(m_loadAnimSubCt){
        case 0: 
        case 1: 
            // Do something but fall through to the other cases 
            // after doing it.
        case 2:
        case 3:
        case 4:
            // Do something else.
            break;
   }

このように、ケースが0、1の場合は常に、switchステートメントのすべてを実行します。私はこれを設計上見てきましたが、switchステートメントをこのように使用することに同意するかどうかはわかりません。最初のコード例は非常に便利で安全だと思います。 2番目は危険なようです。

107
Fostah

それはあなたがフォールスルーと考えるものに依存するかもしれません。私はこの種のもので大丈夫です:

switch (value)
{
  case 0:
    result = ZERO_DIGIT;
    break;

  case 1:
  case 3:
  case 5:
  case 7:
  case 9:
     result = ODD_DIGIT;
     break;

  case 2:
  case 4:
  case 6:
  case 8:
     result = EVEN_DIGIT;
     break;
}

しかし、ケースラベルの後に別のケースラベルに至るコードが続く場合、私はほとんど常にその悪を考えます。おそらく、共通のコードを関数に移動し、両方の場所から呼び出す方が良いでしょう。

そして、私は C++ FAQ"evil" の定義を使用していることに注意してください

79
Fred Larson

それは両刃の剣です。時には非常に便利で、しばしば危険です。

いついいですか? 10ケースすべてを同じ方法で処理したい場合...

switch (c) {
  case 1:
  case 2:
            ... do some of the work ...
            /* FALLTHROUGH */
  case 17:
            ... do something ...
            break;
  case 5:
  case 43:
            ... do something else ...
            break;
}

私が気に入っているルールの1つは、休憩を除外するような空想的なことをした場合、明確なコメント/ * FALLTHROUGH * /が必要であることを示すことです。

50
John M

フォールスルーは、何をしているのかにもよりますが、本当に便利です。オプションを整理するこのきちんとした理解可能な方法を検討してください。

switch ($someoption) {
  case 'a':
  case 'b':
  case 'c':
    // do something
    break;
  case 'd':
  case 'e':
    // do something else
    break;
}

If/elseでこれを行うことを想像してください。それは混乱になります。

20
Lucas Oman

Duffのデバイス を聞いたことがありますか?これは、スイッチフォールスルーを使用する優れた例です。

これは、ほとんどすべての言語機能と同様に、使用できる機能であり、悪用される可能性があります。

20
tzot

これは数回非常に役立つことがありますが、一般的には、フォールスルーなしが望ましい動作です。フォールスルーは許可されるべきですが、暗黙的ではありません。

例、一部のデータの古いバージョンを更新するには:

switch (version) {
    case 1:
        // update some stuff
    case 2:
        // update more stuff
    case 3:
        // update even more stuff
    case 4:
        // and so on
}
10
Mister

スイッチのフォールバック用の別の構文(errrなど)が必要です。

switch(myParam)
{
  case 0 or 1 or 2:
    // do something;
    break;
  case 3 or 4:
    // do something else;
    break;
}

注:フラグを使用して列挙型のすべてのケースを正しく宣言する場合、列挙型で既に可能ですか?どちらかと言えば悪くない場合、ケースは既に列挙型の一部である可能性があります(すべきですか?)

多分これは、拡張メソッドを使用する流fluentなインターフェイスのニースケース(しゃれなし)でしょうか?なんか...

int value = 10;
value.Switch()
  .Case(() => { /* do something; */ }, new {0, 1, 2})
  .Case(() => { /* do something else */ } new {3, 4})
  .Default(() => { /* do the default case; */ });

それはおそらくさらに読みにくいですが:P

6
Erik van Brakel

他のものと同様に、注意して使用すると、エレガントなツールになります。

ただし、欠点はそれを使用しないことを正当化することよりも、最終的にはもう許可しないこと(C#)を正当化することよりも多いと思います。問題には次のものがあります。

  • 休憩を「忘れる」のは簡単です
  • 省略されたブレークが意図的なものであったことは、コード管理者にとって常に明らかではありません

スイッチ/ケースフォールスルーの適切な使用:

switch (x)
{
case 1:
case 2:
case 3:
 do something
 break;
}

スイッチ/ケースフォールスルーのBAAAAAD使用:

switch (x)
{
case 1:
    some code
case 2:
    some more code
case 3:
    even more code
    break;
}

私の意見では、これはif/else構文を使用して書き換えることができます。

私の最後の言葉:BADの例のように、このスタイルが使用され、よく理解されているレガシコードを維持している場合を除き、フォールスルーケースラベルには近づかないでください。

5
steffenj

強力で危険です。フォールスルーの最大の問題は、明示的ではないことです。たとえば、フォールスルーのあるスイッチを持つ頻繁に編集されるコードに出くわした場合、それが意図的なものでありバグではないことをどうやって知るのでしょうか?

どこで使用しても、適切にコメントされていることを確認します。

switch($var) {
    case 'first':
        // fall-through
    case 'second':
        i++;
        break;
 }
4
Dan Hulton

最初の例のようにフォールスルーを使用することは明らかに問題ありません。実際のフォールスルーとは考えません。

2番目の例は危険であり、(広範囲にコメントしない限り)非自明です。私は生徒にこのような構成を使用しないように教えます。コメントブロックを使用する価値があると考えない限り、これは意図的なフォールスルーであり、このソリューションが代替よりも優れている理由を説明します。これは、ずさんな使用を思いとどまらせますが、有利に使用される場合でも許可されます。

これは、誰かがコーディング標準に違反したいときに宇宙プロジェクトでやったことと多かれ少なかれ同等です:彼らは分配を申請しなければなりませんでした(そして私は裁定について助言を求められました)。

3

switchステートメントが失敗するのは好きではありません。エラーが発生しやすく、読みにくいです。唯一の例外は、複数のcaseステートメントがすべて正確に同じことを行う場合です。

Switchステートメントの複数のブランチが使用する一般的なコードがある場合、任意のブランチで呼び出すことができる別の共通関数にそれを抽出します。

2
Matt Dillard

fall thoughtは、コードブロックへのジャンプテーブルとして使用する場合にのみ使用してください。他のケースの前に無条件のブレークがあるコードの部分がある場合、すべてのケースグループはそのように終了する必要があります。それ以外は「悪」です。

1
BCS

場合によっては、フォールスルーの使用はプログラマー側の怠actな行為です。一連の||を使用できます。たとえば、ステートメントですが、代わりに一連の「キャッチオール」スイッチケースを使用します。

そうは言っても、最終的にとにかくオプションが必要になる(たとえばメニュー応答で)ことを知っているときに、特に役立つことがわかりましたが、まだすべてを実装していません選択肢。同様に、「a」と「A」の両方でフォールスルーを実行している場合、複合ifステートメントよりもスイッチフォールスルーを使用する方がかなりクリーンであることがわかります。

それはおそらくスタイルとプログラマーの考え方の問題ですが、私は一般に「安全」という名の言語のコンポーネントを削除するのが好きではありません-それは私が言うよりもCとそのバリアント/子孫に向かう傾向がある理由ですJava。 「理由」がなくても、ポインターなどを使って猿を回すことができるのが好きです。

1
warren