C# でステートメントを使用する場合と比較して、switch
ステートメントを使用する場合の利点/欠点は何ですか。if/else
おそらくコードの外観以外に、それほど大きな違いがあるとは想像できません。
結果として得られる IL または関連するランタイム パフォーマンスが根本的に異なる理由はありますか?
C# でステートメントを使用する場合と比較して、switch
ステートメントを使用する場合の利点/欠点は何ですか。if/else
おそらくコードの外観以外に、それほど大きな違いがあるとは想像できません。
結果として得られる IL または関連するランタイム パフォーマンスが根本的に異なる理由はありますか?
SWITCH ステートメントは、デバッグ モードまたは互換モードの IF と同じアセンブリのみを生成します。リリースでは、(MSIL 'switch' ステートメントを介して) ジャンプ テーブルにコンパイルされます。これは O(1) です。
C# (他の多くの言語とは異なり) では、文字列定数をオンにすることもできますが、これは少し異なる動作をします。任意の長さの文字列のジャンプ テーブルを構築することは明らかに実用的ではないため、ほとんどの場合、そのようなスイッチは IF のスタックにコンパイルされます。
ただし、条件の数がオーバーヘッドをカバーするのに十分な場合、C# コンパイラは HashTable オブジェクトを作成し、それに文字列定数を入力して、そのテーブルを検索し、その後ジャンプします。ハッシュテーブル ルックアップは厳密には O(1) ではなく、顕著な定数コストがありますが、ケース ラベルの数が多い場合は、IF 内の各文字列定数と比較するよりも大幅に高速になります。
要約すると、条件の数が 5 つ以上の場合は、IF よりも SWITCH を優先し、それ以外の場合は、見栄えの良いものを使用します。
一般に (すべての言語とすべてのコンパイラを考慮して)、コンパイラが 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」の投稿に投票してください。
を好む 3 つの理由switch
:
ネイティブ コードを対象とするコンパイラは、多くの場合、switch ステートメントを1 つの条件付き分岐と間接ジャンプにコンパイルできますが、一連のif
s には一連の条件付き分岐が必要です。ケースの密度に応じて、ケースステートメントを効率的にコンパイルする方法について非常に多くの学習論文が書かれています。一部はlcc コンパイラ ページからリンクされています。(Lcc には、スイッチ用のより革新的なコンパイラの 1 つがありました。)
switch ステートメントは、相互に排他的な選択肢の中から選択するものであり、switch 構文により、この制御フローが if-then-else ステートメントのネストよりもプログラマーに対して透過的になります。
間違いなく ML や Haskell を含む一部の言語では、コンパイラは case を除外していないかどうかを確認します。私は、この機能を ML と Haskell の主要な利点の 1 つと考えています。C# でこれができるかどうかはわかりません。
逸話: 生涯功労賞を受賞した際の講演で、トニー・ホーアは、彼のキャリアで行ったすべてのことの中で、最も誇りに思っていることが 3 つあったと語っているのを聞きました。
case
ステートメントと呼びました)なしの生活は想像できませswitch
ん。
コンパイラはほとんどすべてを同じコードに最適化しますが、わずかな違いがあります (Knuth、誰か?)。
違いは、switch ステートメントは、else ステートメントが連続した 15 よりもクリーンであるということです。
友達は、友達に if-else ステートメントをスタックさせません。
実際には、switch ステートメントの方が効率的です。コンパイラは、if/else ステートメントではできないルックアップ テーブルに最適化します。欠点は、switch ステートメントを変数値で使用できないことです。
あなたはできません:
switch(variable)
{
case someVariable
break;
default:
break;
}
そうでなければならない
switch(variable)
{
case CONSTANT_VALUE;
break;
default:
break;
}
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 に変えたとしても、単純にゼロをチェックするほど高速ではありません。
多くの場合、見栄えが良くなります。つまり、何が起こっているのかを理解しやすくなります。パフォーマンス上の利点がせいぜい最小限であることを考えると、コードのビューが最も重要な違いです。
そのため、if/else の方が良さそうな場合はそれを使用し、そうでない場合は switch ステートメントを使用します。
余談ですが、私はよく心配します (そして、より頻繁に見ます) if
/ else
andswitch
ステートメントがあまりにも多くのケースで大きくなりすぎます。これらはしばしば保守性を損ないます。
一般的な犯人は次のとおりです。
修正するには:
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;
}
switch ステートメントは、if else if よりも確実に高速です。BlackWasp によって提供されたスピードテストがあります
http://www.blackwasp.co.uk/SpeedTestIfElseSwitch.aspx
- 見てみな
ただし、考慮しようとしている可能性に大きく依存しますが、可能な限り switch ステートメントを使用するようにしています。
これは実際にあなたの質問に答えるものではありませんが、コンパイルされたバージョン間にほとんど違いがないことを考えると、あなたの意図を最もよく説明する方法でコードを書くことをお勧めします. コンパイラが期待どおりに動作する可能性が高くなるだけでなく、他のユーザーがコードを保守しやすくなります。
1 つの変数/属性の値に基づいてプログラムを分岐することが意図されている場合は、switch ステートメントがその意図を最もよく表しています。
異なる変数/属性/条件に基づいてプログラムを分岐することが意図されている場合は、if/else if チェーンがその意図を最もよく表しています。
人々が break コマンドを忘れていることについては cody が正しいことは認めますが、{ } を間違える複雑な if ブロックをしている人をほぼ同じくらい頻繁に見かけます。これは、if ステートメントに 1 行でも常に{ } を含める理由の 1 つです。読みやすくなるだけでなく、条件に別の行を追加する必要がある場合は、忘れずに追加します。
興味のある質問。これは数週間前に職場で発生し、サンプル スニペットを作成して .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 ステートメントを使用することです。たとえば、次のようになります。
値を操作して switch ステートメントに入力する必要がある場合 (切り替え対象の一時変数を作成する) は、おそらく if/else 制御ステートメントを使用する必要があります。
アップデート:
実際には、文字列を大文字 (例: ToUpper()
)に変換することをお勧めしますToLower()
。これはマイクロ最適化ですが、タイトなループでは役立つ可能性があります。
ちょっとした補足:
switch ステートメントの読みやすさを改善するには、次のことを試してください。
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 テストと等価テストを計算する必要がないことをコンパイラに暗黙的に伝えているためです。
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 を学ぶ前にそれを学びました。これらは近い言語です。
私が気付いたのは、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;
}
私のCS教授は、ステートメントを切り替えないように提案しました。なぜなら、人々はブレークを忘れたり、間違って使用したりすることが多いからです。彼が言ったことを正確に思い出すことはできませんが、(数年前に) switch ステートメントの例を示した重要なコード ベースを見ると、そこにもたくさんの間違いがありました。