この質問は主にC/C++を指していますが、他の言語も同様に関連していると思います。
If/else ifの代わりにswitch/caseがまだ使用されている理由を理解できません。 gotoを使用するのと同じように思え、同じ種類の乱雑なコードが生成されますが、if/else ifを使用すると同じ結果がはるかに体系化された方法で実現できます。
それでも、これらのブロックは頻繁に見られます。それらを見つける一般的な場所はメッセージループ(WndProc ...)の近くです内部で初期化されます)。休憩を落とさないように特別な注意を払う必要があります...
個人的に、私はそれらを使用することを避けます、そして、私は何かを見逃しているのだろうか?
If/elseよりも効率的ですか?彼らは伝統によって続けられていますか?
最初の投稿とコメントの要約-switch
/if
ステートメントに対するelse
ステートメントの利点はいくつかあります。
よりクリーンなコード。複数のチェーンif
/else if ...
は乱雑に見え、保守が困難です-switch
はよりクリーンな構造を提供します。
パフォーマンス。密なcase
値の場合、コンパイラはスパース-バイナリ検索または一連のif
/else
のジャンプテーブルを生成するため、最悪の場合switch
はif
/else
ですが、通常は高速です。一部のコンパイラはif
/else
を同様に最適化できますが。
テストの順序は関係ありません。一連のif
/else
テストを高速化するには、より可能性の高いケースを最初に置く必要があります。 switch
/case
を使用すると、プログラマはこれについて考える必要がありません。
デフォルトはどこでもかまいません。 if
/else
の場合、デフォルトのケースは最後のelse
の後でなければなりません。 switch
では-default
はどこでも、プログラマーがより適切だと思うところならどこでもかまいません。
共通コード。いくつかのケースで共通のコードを実行する必要がある場合、break
を省略すると、実行は「フォールスルー」します。これはif
/else
では実現できません。 (特別なコメントを配置することをお勧めします/* FALLTHROUGH */
このような場合-lintはそれを認識し、文句を言いません。このコメントがなければ、break
を忘れるのはよくあるエラーなので文句を言います。
すべてのコメント者に感謝します。
まあ、一つの理由は明快さです。..
スイッチ/ケースがある場合、式は変更できません。
switch (foo[bar][baz]) {
case 'a':
...
break;
case 'b':
...
break;
}
一方、if/elseを使用して、誤って(または意図)を記述した場合:
if (foo[bar][baz] == 'a') {
....
}
else if (foo[bar][baz+1] == 'b') {
....
}
あなたのコードを読んでいる人は、「foo式は同じであるはずだったのだろうか」、「なぜ違うのか」と疑問に思うでしょう。
ケース/選択により柔軟性が高まることを忘れないでください:
と同様に、より高速に実行されます(ジャンプ/ルックアップテーブル経由)*歴史的に
また、switchステートメントを使用すると、制御のフローを継続できるため、条件をうまく組み合わせながら、次のコードのように特定の条件に追加のコードを追加できます。
switch (dayOfWeek)
{
case MONDAY:
garfieldUnhappy = true;
case TUESDAY:
case WEDNESDAY:
case THURSDAY:
case FRIDAY:
weekDay = true;
break;
case SATURDAY:
weekendJustStarted = true;
case SUNDAY:
weekendDay = true;
break;
}
代わりにここでif/else
ステートメントを使用することは、ニースほどではありません。
if (dayOfWeek == MONDAY)
{
garfieldUnhappy = true;
}
if (dayOfWeek == SATURDAY)
{
weekendJustStarted = true;
}
if (dayOfWeek == MONDAY || dayOfWeek == TUESDAY || dayOfWeek == WEDNESDAY
|| dayOfWeek == THURSDAY || dayOfWeek == FRIDAY)
{
weekDay = true;
}
else if (dayOfWeek == SATURDAY || dayOfWeek == SUNDAY)
{
weekendDay = true;
}
多数のケースがある場合、switchステートメントは簡潔に見えます。
同じ振る舞いをしたい複数の値がある場合もいいです-単一の実装に至る複数の「case」ステートメントを使用するだけで、if(this || that || something || .. )
また、言語にも依存する場合があります-たとえば、一部の言語スイッチは数値型でのみ動作するため、列挙値、数値定数などを操作するときに入力を節約できます.
If (day == DAYOFWEEK_MONDAY) {
//...
}
else if (day == DAYOFWEEK_TUESDAY) {
//...
}
//etc...
または少し読みやすい...
switch (day) {
case DAYOFWEEK_MONDAY :
//...
case DAYOFWEEK_TUESDAY :
//...
//etc...
}
通常、Switch/caseはif/else if/elseよりも効率的に最適化されますが、ときどき(言語とコンパイラに応じて)単純なif/else if/elseステートメントに変換されます。
私は個人的に、switchステートメントはifステートメントよりもコードを読みやすくすると考えています。ただし、いくつかの簡単なルールに従う必要があります。 if/else if/elseの状況でも、おそらく従うべき規則ですが、それも私の意見です。
それらのルール:
明快さ。 here と言ったように、else if
は問題がある
eLSE IFが構文で許可されるよりもはるかに制約された方法で使用される頻度。これは柔軟性の大ハンマーであり、まったく無関係な条件をテストできます。しかし、同じ式を別の値と比較して、CASEのハエを叩くために日常的に使用されます...
これにより、コードの可読性が低下します。構造は条件付き複雑さの世界を許容するため、読者はCASEを解析するときよりもELSE IFを解析するときにより多くの可能性を念頭に置く必要があります。
実際、switchステートメントは、何が起こっているのかを即座に示す手掛かりとなる、多少なりとも列挙型であることに取り組んでいることを意味します。
つまり、任意のOO言語の列挙型のスイッチはおそらくより適切にコーディングできます。同じ一連のif/elseの同じ "enum"スタイル値は、少なくとも同じくらい悪いでしょう。意味を伝えることでさらに悪い。
スイッチ内のすべてに同等のスコープがあるという懸念に対処するため、ケースロジックを別の{}ブロックにいつでもスローできます。
switch( thing ) {
case ONETHING: {
int x; // local to the case!
...
}
break;
case ANOTHERTHING: {
int x; // a different x than the other one
}
break;
}
..今、私はそれがきれいだと言っているわけではありません。ある場合に何かを絶対に分離する必要がある場合は、それをpossibleとしてそこに置くだけです。
スコープの問題に関するもう1つの考え-関数内に1つのスイッチのみを配置し、他の多くのスイッチは配置しないことをお勧めします。そのような状況では、変数スコープはそれほど関心事ではありません。その方法では、通常、関数の特定の呼び出しで実行されるケースが1つだけであるためです。
さて、スイッチについて最後に考えました。関数に複数のスイッチが含まれている場合は、おそらくコードをリファクタリングするときです。関数にnestedスイッチが含まれる場合、おそらくデザインを少し考え直すための手がかりになります=)
スイッチケースは、主にプログラミングで選択するために使用されます。これは、条件文とは関係ありません。
プログラムで選択するだけでif/elseブロックを使用し、プログラミングの労力を増やすだけでなく、プログラムの実行速度が低下する場合。
Smalltalkerはスイッチとif-then-elseの両方を拒否し、次のように記述します。
shortToLongDaysMap := Dictionary new.
shortToLongDaysMap
at: 'Mon' put: 'Monday';
at: 'Tue' put: 'Tuesday';
at: 'Wed' put: 'Wednesday'
etc etc.
longForm := shortToLongDaysMap at: shortForm ifAbsent: [shortForm]
これは些細な例ですが、この手法が多数のケースでどのように拡大縮小するかをご覧ください。
at:IfAbsent:
の2番目の引数は、caseステートメントのデフォルト句に似ていることに注意してください。
この背後にある主な理由は、保守性と可読性です。 Switch/caseステートメントを使用してif/elseを使用すると、コードを読みやすく保守しやすくなります。多くのif/elseがあるため、コードはネストのように非常に乱雑になり、維持するのが非常に難しくなります。
また、実行時間がもう1つの理由であることがあります。
if
/else if
と同じものにコンパイルされることはかなり確かですが、2つまたは3つ以上ある場合はswitch
/case
が読みやすいと思いますelse
s。
Switchステートメントは速度を最適化できますが、case値が多数の値に分散している場合、より多くのメモリを消費する可能性があります。
各値をチェックする必要があるため、if/elseは一般に低速です。