253

C# でステートメントを使用する場合と比較して、switchステートメントを使用する場合の利点/欠点は何ですか。if/elseおそらくコードの外観以外に、それほど大きな違いがあるとは想像できません。

結果として得られる IL または関連するランタイム パフォーマンスが根本的に異なる理由はありますか?

関連:文字列のスイッチとタイプのelseifのどちらが速いですか?

4

20 に答える 20

390

SWITCH ステートメントは、デバッグ モードまたは互換モードの IF と同じアセンブリのみを生成します。リリースでは、(MSIL 'switch' ステートメントを介して) ジャンプ テーブルにコンパイルされます。これは O(1) です。

C# (他の多くの言語とは異なり) では、文字列定数をオンにすることもできますが、これは少し異なる動作をします。任意の長さの文字列のジャンプ テーブルを構築することは明らかに実用的ではないため、ほとんどの場合、そのようなスイッチは IF のスタックにコンパイルされます。

ただし、条件の数がオーバーヘッドをカバーするのに十分な場合、C# コンパイラは HashTable オブジェクトを作成し、それに文字列定数を入力して、そのテーブルを検索し、その後ジャンプします。ハッシュテーブル ルックアップは厳密には O(1) ではなく、顕著な定数コストがありますが、ケース ラベルの数が多い場合は、IF 内の各文字列定数と比較するよりも大幅に高速になります。

要約すると、条件の数が 5 つ以上の場合は、IF よりも SWITCH を優先し、それ以外の場合は、見栄えの良いものを使用します。

于 2008-12-28T08:06:05.887 に答える
59

一般に (すべての言語とすべてのコンパイラを考慮して)、コンパイラが switch ステートメントからジャンプ テーブルを生成するのは簡単であるため、switch ステートメントは if / else ステートメントよりも効率的である場合があります。適切な制約があれば、if / else ステートメントに対して同じことを行うことは可能ですが、それははるかに困難です。

C# の場合も同様ですが、別の理由があります。

文字列の数が多い場合、switch ステートメントを使用すると、パフォーマンスが大幅に向上します。これは、コンパイラがハッシュ テーブルを使用してジャンプを実装するためです。

弦の数が少ないため、両者の性能は同じです。

これは、C# コンパイラがジャンプ テーブルを生成しないためです。代わりに、IF / ELSE ブロックと同等の MSIL を生成します。

「switch ステートメント」MSIL 命令があり、jitted 時にジャンプ テーブルを使用して switch ステートメントを実装します。ただし、整数型でのみ機能します(この質問は文字列について尋ねます)。

文字列の数が少ない場合は、ハッシュ テーブルを使用するよりも、コンパイラが IF / ELSE ブロックを生成する方が効率的です。

最初にこれに気付いたとき、IF / ELSE ブロックが少数の文字列で使用されたため、コンパイラが多数の文字列に対して同じ変換を行ったのではないかと推測しました。

これは間違っていました。「IMA」は親切にも私にこれを指摘してくれました (ええと...彼はそれについて親切ではありませんでしたが、彼は正しく、私は間違っていました。これは重要な部分です)

また、MSIL に "switch" 命令がないことについて、骨の折れる仮定を立てました (switch プリミティブがあった場合、なぜハッシュ テーブルでそれを使用しなかったのか、switch プリミティブがあってはならないことを理解しました。 ... )。これは間違いであり、私の側では信じられないほど愚かでした。再び「IMA」はこれを私に指摘しました。

これは最高評価の投稿であり、受け入れられた回答であるため、ここで更新を行いました。

ただし、間違っているために REP に値しないと判断したため、コミュニティ Wiki にしました。機会があれば、「ima」の投稿に投票してください。

于 2008-12-28T01:06:51.880 に答える
19

を好む 3 つの理由switch:

  • ネイティブ コードを対象とするコンパイラは、多くの場合、switch ステートメントを1 つの条件付き分岐と間接ジャンプにコンパイルできますが、一連のifs には一連の条件付き分岐が必要です。ケースの密度に応じて、ケースステートメントを効率的にコンパイルする方法について非常に多くの学習論文が書かれています。一部はlcc コンパイラ ページからリンクされています。(Lcc には、スイッチ用のより革新的なコンパイラの 1 つがありました。)

  • switch ステートメントは、相互に排他的な選択肢の中から選択するものであり、switch 構文により、この制御フローが if-then-else ステートメントのネストよりもプログラマーに対して透過的になります。

  • 間違いなく ML や Haskell を含む一部の言語では、コンパイラは case を除外していないかどうかを確認します。私は、この機能を ML と Haskell の主要な利点の 1 つと考えています。C# でこれができるかどうかはわかりません。

逸話: 生涯功労賞を受賞した際の講演で、トニー・ホーアは、彼のキャリアで行ったすべてのことの中で、最も誇りに思っていることが 3 つあったと語っているのを聞きました。

  • クイックソートの発明
  • switch ステートメントの発明 (Tony はこれをcaseステートメントと呼びました)
  • 業界でのキャリアの開始と終了

なしの生活は想像できませswitchん。

于 2008-12-28T06:27:55.540 に答える
19

コンパイラはほとんどすべてを同じコードに最適化しますが、わずかな違いがあります (Knuth、誰か?)。

違いは、switch ステートメントは、else ステートメントが連続した 15 よりもクリーンであるということです。

友達は、友達に if-else ステートメントをスタックさせません。

于 2008-12-28T00:22:37.697 に答える
17

実際には、switch ステートメントの方が効率的です。コンパイラは、if/else ステートメントではできないルックアップ テーブルに最適化します。欠点は、switch ステートメントを変数値で使用できないことです。
あなたはできません:

switch(variable)
{
   case someVariable
   break;
   default:
   break;
}

そうでなければならない

switch(variable)
{
  case CONSTANT_VALUE;
  break;
  default:
  break;
}
于 2008-12-28T00:20:53.783 に答える
16

switch ステートメントの想定される効率の利点は、さまざまなケースがほぼ同じ確率であることに依存しているという (明らかな?) ポイントを指摘する人は他に見当たりませんでした。値の 1 つ (またはいくつか) の可能性がはるかに高い場合、最も一般的なケースが最初にチェックされるようにすることで、if-then-else はしごがはるかに高速になります。

たとえば、次のようになります。

if (x==0) then {
  // do one thing
} else if (x==1) {
  // do the other thing
} else if (x==2) {
  // do the third thing
}

switch(x) {
  case 0: 
         // do one thing
         break;
  case 1: 
         // do the other thing
         break;
  case 2: 
         // do the third thing
         break;
}

x が 90% の確率でゼロの場合、"if-else" コードは、switch ベースのコードの 2 倍の速さになります。コンパイラが「スイッチ」を何らかの巧妙なテーブル駆動の goto に変えたとしても、単純にゼロをチェックするほど高速ではありません。

于 2009-10-15T22:32:16.880 に答える
8

多くの場合、見栄えが良くなります。つまり、何が起こっているのかを理解しやすくなります。パフォーマンス上の利点がせいぜい最小限であることを考えると、コードのビューが最も重要な違いです。

そのため、if/else の方が良さそうな場合はそれを使用し、そうでない場合は switch ステートメントを使用します。

于 2008-12-28T00:22:54.870 に答える
4

余談ですが、私はよく心配します (そして、より頻繁に見ます) if/ elseandswitchステートメントがあまりにも多くのケースで大きくなりすぎます。これらはしばしば保守性を損ないます。

一般的な犯人は次のとおりです。

  1. 複数の if ステートメント内でやりすぎる
  2. 人間が分析できるよりも多くのケースステートメント
  3. if 評価の条件が多すぎて、何を探しているかがわからない

修正するには:

  1. メソッドのリファクタリングに抽出します。
  2. ケースの代わりにメソッド ポインターを含むディクショナリを使用するか、構成可能性を追加するために IoC を使用します。メソッド ファクトリも役立ちます。
  3. 独自のメソッドに条件を抽出
于 2008-12-28T04:09:01.560 に答える
4

if または else ステートメントを使用しているだけの場合、基本ソリューションは比較を使用していますか? オペレーター

(value == value1) ? (type1)do this : (type1)or do this;

スイッチで or ルーチンを実行できます

switch(typeCode)
{
   case TypeCode:Int32:
   case TypeCode.Int64:
     //dosomething here
     break;
   default: return;
}
于 2010-07-31T17:12:38.057 に答える
2

switch ステートメントは、if else if よりも確実に高速です。BlackWasp によって提供されたスピードテストがあります

http://www.blackwasp.co.uk/SpeedTestIfElseSwitch.aspx

- 見てみな

ただし、考慮しようとしている可能性に大きく依存しますが、可能な限り switch ステートメントを使用するようにしています。

于 2009-10-15T21:30:20.063 に答える
2

これは実際にあなたの質問に答えるものではありませんが、コンパイルされたバージョン間にほとんど違いがないことを考えると、あなたの意図を最もよく説明する方法でコードを書くことをお勧めします. コンパイラが期待どおりに動作する可能性が高くなるだけでなく、他のユーザーがコードを保守しやすくなります。

1 つの変数/属性の値に基づいてプログラムを分岐することが意図されている場合は、switch ステートメントがその意図を最もよく表しています。

異なる変数/属性/条件に基づいてプログラムを分岐することが意図されている場合は、if/else if チェーンがその意図を最もよく表しています。

人々が break コマンドを忘れていることについては cody が正しいことは認めますが、{ } を間違える複雑な if ブロックをしている人をほぼ同じくらい頻繁に見かけます。これは、if ステートメントに 1 行でも常に{ } を含める理由の 1 つです。読みやすくなるだけでなく、条件に別の行を追加する必要がある場合は、忘れずに追加します。

于 2008-12-28T06:21:32.790 に答える
2

興味のある質問。これは数週間前に職場で発生し、サンプル スニペットを作成して .NET Reflector で表示することで答えを見つけました (reflector は最高です!! 私はそれが大好きです)。

これが私たちが発見したことです。文字列以外の有効な switch ステートメントは、switch ステートメントとして IL にコンパイルされます。ただし、文字列の場合、IL では if/else if/else に書き換えられます。したがって、私たちのケースでは、switch ステートメントが文字列をどのように比較するか (大文字と小文字が区別されるなど) を知りたいと思っていましたが、リフレクターはすぐに答えをくれました。これは知っておくと便利でした。

文字列で大文字と小文字を区別して比較する場合は、if/else で String.Compare を実行するよりも高速であるため、switch ステートメントを使用できます。(編集:実際のパフォーマンス テストについては、文字列をオンにするか、型を Elseif に切り替えて、どちらが高速かをお読みください) ただし、大文字と小文字を区別しない場合は、結果のコードがきれいではないため、if/else を使用することをお勧めします。

switch (myString.ToLower())
{
  // not a good solution
}

最良の経験則は、(真剣に) 理にかなっている場合は switch ステートメントを使用することです。たとえば、次のようになります。

  • コードの可読性が向上します
  • 値の範囲 (float、int) または列挙型を比較している

値を操作して switch ステートメントに入力する必要がある場合 (切り替え対象の一時変数を作成する) は、おそらく if/else 制御ステートメントを使用する必要があります。

アップデート:

実際には、文字列を大文字 (例: ToUpper())に変換することをお勧めしますToLower()。これはマイクロ最適化ですが、タイトなループでは役立つ可能性があります。


ちょっとした補足:

switch ステートメントの読みやすさを改善するには、次のことを試してください。

  • 最も可能性の高いブランチを最初に配置します。つまり、最もアクセスされます
  • それらがすべて発生する可能性がある場合は、アルファベット順にリストして、見つけやすくします。
  • 最後の残りの条件にデフォルトのキャッチオールを使用しないでください。これは怠惰であり、コードの寿命の後半で問題を引き起こします。
  • デフォルトのキャッチオールを使用して、発生する可能性が非常に低い場合でも、未知の状態をアサートします。それがアサーションの良いところです。
于 2008-12-28T11:54:36.237 に答える
1

C# だけでなく、すべての C ベースの言語だと思います。スイッチは定数に限定されているため、「ジャンプ テーブル」を使用して非常に効率的なコードを生成できます。C の場合は、実際には古き良き FORTRAN の計算された GOTO ですが、C# の場合は依然として定数に対するテストです。

オプティマイザーが同じコードを作成できるわけではありません。たとえば、

if(a == 3){ //...
} else if (a == 5 || a == 7){ //...
} else {//...
}

これらは複合ブール値であるため、生成されたコードは値を計算して短絡する必要があります。今、同等のものを考えてみましょう

switch(a){
   case 3: // ...
    break;
   case 5:
   case 7: //...
    break;
   default: //...
}

これはにコンパイルできます

BTABL: *
B3:   addr of 3 code
B5:
B7:   addr of 5,7 code
      load 0,1 ino reg X based on value
      jump indirect through BTABL+x

これは、OR テストと等価テストを計算する必要がないことをコンパイラに暗黙的に伝えているためです。

于 2008-12-28T00:21:45.887 に答える
0

switch ステートメントは、基本的に等価性の比較です。キーボード イベントは、コードの記述と読み取りが簡単な場合、switch ステートメントよりも大きな利点があります。if elseif ステートメントの場合は {bracket} の欠落も問題になる可能性があります。

char abc;
switch(abc)
{
case a: break;
case b: break;
case c: break;
case d: break;
}

if(theAmountOfApples が 5 より大きい && theAmountOfApples が 10 より小さい) リンゴを保存する if(theAmountOfApples が 10 より大きい || theAmountOfApples == 100) リンゴを売る. 私は c# や c++ を書きませんが、Java を学ぶ前にそれを学びました。これらは近い言語です。

于 2015-04-25T01:49:54.417 に答える
0

私が気付いたのは、if/else ステートメントと switch ステートメントを組み合わせることができるということです。前提条件を確認する必要がある場合に非常に便利です。

if (string.IsNullOrEmpty(line))
{
    //skip empty lines
}
else switch (line.Substring(0,1))
{
    case "1":
        Console.WriteLine(line);
        break;
    case "9":
        Console.WriteLine(line);
        break;
    default:
        break;
}
于 2009-10-02T13:48:25.783 に答える
0

私のCS教授は、ステートメントを切り替えないように提案しました。なぜなら、人々はブレークを忘れたり、間違って使用したりすることが多いからです。彼が言ったことを正確に思い出すことはできませんが、(数年前に) switch ステートメントの例を示した重要なコード ベースを見ると、そこにもたくさんの間違いがありました。

于 2008-12-28T00:45:37.710 に答える