10 に答える
DoSomething
2 に設定すると、それらx
は異なります。
[STAThread]
public static void Main()
{
Int32 x = 1;
if (x == 1)
Console.WriteLine("1");
else if (x == 2)
Console.WriteLine("2");
}
結果:
.method public hidebysig static void Main() cil managed
{
.custom instance void [mscorlib]System.STAThreadAttribute::.ctor()
.entrypoint
.maxstack 2
.locals init (
[0] int32 x)
L_0000: ldc.i4.1
L_0001: stloc.0
L_0002: ldloc.0
L_0003: ldc.i4.1
L_0004: bne.un.s L_0011
L_0006: ldstr "1"
L_000b: call void [mscorlib]System.Console::WriteLine(string)
L_0010: ret
L_0011: ldloc.0
L_0012: ldc.i4.2
L_0013: bne.un.s L_001f
L_0015: ldstr "2"
L_001a: call void [mscorlib]System.Console::WriteLine(string)
L_001f: ret
}
その間:
[STAThread]
public static void Main()
{
Int32 x = 1;
if (x == 1)
Console.WriteLine("1");
if (x == 2)
Console.WriteLine("2");
}
結果:
.method public hidebysig static void Main() cil managed
{
.custom instance void [mscorlib]System.STAThreadAttribute::.ctor()
.entrypoint
.maxstack 2
.locals init (
[0] int32 x)
L_0000: ldc.i4.1
L_0001: stloc.0
L_0002: ldloc.0
L_0003: ldc.i4.1
L_0004: bne.un.s L_0010
L_0006: ldstr "1"
L_000b: call void [mscorlib]System.Console::WriteLine(string)
L_0010: ldloc.0
L_0011: ldc.i4.2
L_0012: bne.un.s L_001e
L_0014: ldstr "2"
L_0019: call void [mscorlib]System.Console::WriteLine(string)
L_001e: ret
}
IL コードは少し異なります。主な違いは次のとおりです。
Approach One: L_0004: bne.un.s L_0011 -> L_0011: ldloc.0 with L_0010: ret
Approach Two: L_0004: bne.un.s L_0010 -> L_0010: ldloc.0 with no ret in between
最初のアプローチのようにelseステートメントを使用すると、条件に一致する最初のブランチのみが実行されます。一方... 2番目のアプローチでは、すべてのチェックが処理され、条件を満たすすべてのチェックが追跡されて処理されます。それが主な違いです。
そのため、最初のアプローチの IL コードでは、Console.WriteLine の呼び出しの直後に「ret」ディレクティブがあり、2 番目のアプローチでは存在しません。最初のケースでは、チェックが通過した直後にメソッドを終了できます。これ以上チェックx
が実行されないためです... 2番目のアプローチでは、それらすべてを順番にたどる必要があるため、 ret はメソッド、最後まで「近道」はありません。
私のテストでは、Console.WriteLine()
呼び出しを使用しました...しかし、変数DoSomething()
の値の変更が含まれる場合x
、コードの動作において違いが絶対に重要になることは確かです。ローカル変数ではなく、プライベートな静的メンバー (初期値は常に 1) として x があるとします。
public void DoSomething()
{
++m_X;
}
最初のアプローチでは、最初のチェックのおかげで が呼び出されたm_X
後に値が 2 であると仮定しても、それ以外の場合はメソッドが終了し、呼び出されることはありません。2 番目のアプローチでは、両方のメソッドが呼び出されます。DoSomething()
DoSomethingElse()
else if
C# にはコンストラクトがないことに注意してください。最初のコード サンプルは次とまったく同じです。
if (x == 1)
DoSomething();
else
{
if (x == 2)
DoSomethingElse();
}
にはステートメントが 1 つしかないためelse
、中かっこは省略できます。読みやすくするために、if
通常は前の と同じ行に記述しelse
ます。複数の " else if
" ステートメントを記述することは、さらにネストすることと同じです。
if (x == 1)
DoSomething();
else
{
if (x == 2)
DoSomethingElse();
else
{
if (x == 3)
YetSomethingElse();
else
{
if (x == 4)
ReallyDifferent();
}
}
}
上記は次のように記述できます。
if (x == 1)
DoSomething();
else if (x == 2)
DoSomethingElse();
else if (x == 3)
YetSomethingElse();
else if (x == 4)
ReallyDifferent();
このことから、チェーン " else if
"if
が異なる結果を生成できることがわかります。" else if
" の場合、条件を満たす最初の分岐が実行され、その後はそれ以上のチェックは行われません。連鎖if
ステートメントの場合、条件を満たすすべての分岐が実行されます。
ここでの主な違いは、分岐の実行によって後続の条件が true になるタイミングです。例えば:
var x = 1;
if (x == 1)
x = 2;
else if (x == 2)
x = 3;
VS
var x = 1;
if (x == 1)
x = 2;
if (x == 2)
x = 3;
最初のケースでx == 2
は 、2 番目のケースではx == 3
.
このようにコーディングすると
// approach two
if (x == 1)
DoSomething();
if (x == 2)
DoSomethingElse();
状態がチェックされるたびに。
しかし、このようにコーディングすると
if (x == 1)
DoSomething();
else if (x == 2)
DoSomethingElse();
最初の条件が true の場合、次の else if 条件をチェックしないため、不要なコンパイルが減少します。
ステートメントを使用するelse
と、分岐の 1 つだけが実行されます (つまり、if
条件に一致する最初の分岐)。他のすべてのif
条件は推定さえされません。
// approach one
int x = 1;
if (x == 1)
DoSomething(); //only this will be run, even if `DoSomething` changes `x` to 2
else if (x == 2)
DoSomethingElse();
使用しない場合は、(各条件に応じて) それぞれが実行される場合があります。つまり、それぞれが 1 つずつ推定されます。
// approach two
int x = 1;
if (x == 1)
DoSomething();//this is run, as `x` == 1
if (x == 2)
DoSomethingElse();//if `DoSomething` changes `x` to 2, this is run as well
そのため、IL は異なる場合があります。
パフォーマンスに関する回答はありませんか?
したがって、x=1 の場合、最初のケースでは 1 つのチェックのみを実行し、2 番目のケースでは 2 つのチェックを実行するため、最初のケースの方が高速です。
x が変更された場合 (Do Seomthing および DoSomethingElse で)、最初のステートメントは 1 つのステートメントのみを実行します。2 番目の例では、すべてのステートメントがチェックされます (もちろん、コンパイラが数値比較のためにジャンプ テーブルに最適化しない限り)。
multiple を使用するelse if
と、条件を満たす条件を実行します。残りのケースがある場合、それらはスキップされます。が複数if
ある場合は、すべてのステートメントをチェックします。したがって、これはパフォーマンスの問題になります。
x
が複数のスレッドによって変更された場合、2 番目のアプローチで と の両方が呼び出される可能性がDoSomething()
ありますDoSomethingElse()