フォールスルースイッチステートメントの適切な使用


14

フォールスルー(クラシック)switchステートメントを使用するのが適切な場合 そのような使用は推奨され奨励されていますか、それともすべてのコストで避けるべきですか?


すべての言語がswitchステートメントでフォールスルーを許可するわけではありません。
オデッド

@Oded、編集、「クラシック」ワードの追加。いないすべての言語がフォールスルーできるように、それにもかかわらず、私はそれは古典的な)であると主張
shabunc

3
Duffsのデバイスについて話している場合、それは1つのことです。
-Oded

6
@Oded:それはほとんどの人が最初に考えることですが、それはほとんど「適切」ではありません。よると、この、ダフ自身が言った、「これは間違いなくスイッチは、デフォルトでフォールスルーする必要があるかどうかの議論で引数を提供していますが、引数がまたはそれに反している場合、私はわかりません。」
BlueRaja-ダニーPflughoeft

回答:


15

これが役に立つ例です。

public Collection<LogItems> GetAllLogItems(Level level) {
    Collection<LogItems> result = new Collection<LogItems>();
    switch (level) {
        // Note: fall through here is INTENTIONAL
        case All:
        case Info:
             result.Add(GetItemsForLevel(Info));
        case Warning:
             result.Add(GetItemsForLevel(Warning));
        case Error:
             result.Add(GetItemsForLevel(Error));
        case Critical:
             result.Add(GetItemsForLevel(Critical));
        case None:
    }
    return result;
}

この種のもの(1つのケースに他のケースが含まれる場合)はかなりまれであると思うので、一部の新しい言語ではフォールオーバーが許可されないか、特別な構文が必要になります。


13
この例は興味深いですが、必要なすべて大文字の// INTENTIONAL FALL THROUGH HEREコメントがありません。
ラッセルボロゴーブ

GetItemsForLevel(Info)呼び出しを呼び出すGetItemsForLevel(Warning)などして、このコードを簡単に作成できます。
カレブ

@RussellBorogove:まったく正しい。
コンフィギュレー

@カレブ:この場合、はい。しかし、呼び出しがもう少し複雑だったらどうでしょうか?それは少し毛むくじゃらになる可能性がありますが、フォールスルーはシンプルに見えるようになります。私は実際にフォールスルーを使用することに賛成していないことに注意する必要があります-私はそれが特定の状況で有用であると言っているだけです。
コンフィギュレー

最良の例ではありません。警告レベルの順序があります。この順序を定義することにより、このメソッドは、アイテムレベルが少なくとも目的のレベルに等しい場合に、アイテムをフィルタリングするコードの1行に削減します。
ケビンクライン

8

特定の機能を複数の値に適用する必要がある場合に使用します。たとえば、operationCodeというプロパティを持つオブジェクトがあるとします。コードが1、2、3、または4に等しい場合、startOperationX()が必要です。5または6の場合、startOperationY()を開始し、7はstartOperationZ()を開始します。フォールスルーを使用できるのに、機能とブレークを含む7つの完全なケースがあるのはなぜですか?

特定の状況では、特に100個のif-elseステートメントを回避する場合、完全に有効だと思います。=)


4
あなたが説明しているのは、同じコードにswitch複数caseのが関連付けられていることです。それはフォールスルーとは異なります。フォールスルーでは、1つの実行がcase次の実行に継続しbreakます。
Blrfl

3
実際には問題ではありません。フォールスルーは単にbreakステートメントがないため、次のケースに「フォールスルー」します。
ヤトリックス

それを反映するために、回答のシナリオを書き直します。
Blrfl

2
@Yatrix:私は同意しません。連続したケースですべて同じコードブロックを実行することは、ブレークを省略することとは大きく異なります。1つはルーチンであり、もう1つはそれ(ime)から遠く離れています-そして、誤読や意図しない制御フローの可能性ははるかに大きくなります。
クエンティン・スターリン

3
@qesはい、違いますが、どちらもフォールスルーです。彼はいつ/いつそれが適切か尋ねました。いつになるか例を示しました。これは包括的な例ではありませんでした。言語によっては、フォールスルーの前にステートメントがあると機能しない場合があります。C#stackoverflow.com
questions/174155/

8

私は時々それらを使用しましたが、常に適切な使用法だと思います-ただし、適切なコメントに含まれている場合のみです。


7

フォールスルーケースはまったく問題ありません。多くの場合、列挙は多くの場所で使用され、いくつかのケースを区別する必要がない場合、フォールスルーロジックを使用する方が簡単であることがわかります。

例(説明コメントに注意してください):

public boolean isAvailable(Server server, HealthStatus health) {
  switch(health) {
    // Equivalent positive cases
    case HEALTHY:
    case UNDER_LOAD:
      return true;

    // Equivalent negative cases
    case FAULT_REPORTED:
    case UNKNOWN:
    case CANNOT_COMMUNICATE:
      return false;

    // Unknown enumeration!
    default:
      LOG.warn("Unknown enumeration " + health);
      return false;
  }
}

この種の使用は完全に受け入れられると思います。


間には大きな差があることに注意してくださいcase X: case Y: doSomething()case X: doSomething(); case Y: doAnotherThing()。最初の場合、意図は非常に明示的ですが、2番目の場合、フォールスルーは意図的である場合とそうでない場合があります。私の経験では、最初の例ではコンパイラ/コードアナライザーで警告がトリガーされることはありませんが、2番目の例ではトリガーされます。個人的には、2番目の例を「フォールスルー」と呼ぶだけです。ただし、「公式」の定義についてはわかりません。
イェンスバンマン

6

による:

  • あなたの個人的な好み
  • 雇用主のコーディング標準
  • 関与するリスクの量

1つのケースを次のケースに移行させることに関連する2つの主な問題は次のとおりです。

  1. コードをcaseステートメントの順序に依存させます。決して失敗しない場合はそうではなく、しばしば歓迎されない程度の複雑さを追加します。

  2. 1つのケースのコードに1つ以上の後続のケースのコードが含まれていることは明らかではありません。

いくつかの場所は明示的に転倒を禁止しています。そのような場所で仕事をしておらず、練習に慣れていて、問題のコードを壊しても実際の苦痛が生じないなら、それは世界で最悪のものではないかもしれません。ただし、それを行う場合は、注意を引くコメントを近くに置いて、後で来る人(将来のあなたを含む)に警告するようにしてください。


4

ここに、私の人生をよりシンプルにするフォールスルーの簡単な(確かに不完全な(うるう年の特別な処理なし))例があります:

function get_julian_day (date) {
    int utc_date = date.getUTCDate();
    int utc_month = date.getUTCMonth();

    int julian_day = 0;

    switch (utc_month) {
        case 11: julian_day += 30;
        case 10: julian_day += 31;
        case 9:  julian_day += 30;
        case 8:  julian_day += 31;
        case 7:  julian_day += 31;
        case 6:  julian_day += 30;
        case 5:  julian_day += 31;
        case 4:  julian_day += 30;
        case 3:  julian_day += 31;
        case 2:  julian_day += 28;
        case 1:  julian_day += 31;
        default: break;
    }

    return julian_day + utc_date;
}

5
これは一種の賢い方法ですが、これを行うことで節約できる時間は、他の人がこれが何をするかを理解するのにかかる時間よりもはるかに重要です。また、フォールスルーアスペクトを削除することでパフォーマンスを向上させることができます。つまり、31 + 28 + 31は常に90です。
ジミージェームズ

私の例は間違いなく不自然です:)しかし、スイッチの各ケースの累積日数をすべて事前に計算するのではなく、このように日数を綴ると、隠れたバグを見つけやすくなります。
アーロンダニエルソン

2
私はあなたにそれを認めますが、定数宣言でそれを行うことが望ましいでしょう、例えばFEB_START = 31; MAR_START = FEB_START + 28; など。これが何の言語かはわかりませんが、多くの場合、コンパイル時に計算されます。ここでの大きな問題は、少し鈍いことです。それは悪い考えではなく、単なる非定型です。本当に大きなメリットがない限り、一般的には一般的なイディオムに固執する方が良いでしょう。
ジミージェームズ

1
この例の開始日に定数を使用する優れた代替手段。とにかく、ここでのポイントは、累積合計がスイッチケースのフォールスルーの優れた用途であることです。私は一般的なイディオムを使用することに100%同意しますが、この質問の性質は、一般的なイディオムが手に入らない難解な機能に挑戦しています。
アーロンダニエルソン

3

あるケースから別のケースに進む必要があると感じた場合(まれに、確かに)、私は非常に明確goto caseにしたいと思います。

落下は非常にまれであり、コードを読んでいるときに見落とすのが非常に簡単なので、明示的にするのが適切であると感じます-そして、場合であっても、後藤は親指のように目立つはずです。

また、caseステートメントが並べ替えられたときに発生する可能性のあるバグを回避するのにも役立ちます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.