10 回答
如果DoSomething
设置x
为 2,那么它们会有所不同。
[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 语句时,与第一种方法一样,只有满足条件的第一个分支才会运行。另一方面......使用第二种方法处理每张支票,并且每张符合条件的支票都将被遵循和处理。这是主要的区别。
这就是为什么在第一种方法的 IL 代码中,您在调用 Console.WriteLine 之后有一个“ret”指令,而在第二种方法中它不存在。在第一种情况下,可以在通过检查后立即退出该方法,因为x
将不再执行检查...在第二种方法中,您必须依次遵循所有这些方法,这就是为什么 ret 只出现在末尾方法,没有“捷径”到最后。
对于我的测试,我使用了一个Console.WriteLine()
调用...但可以肯定的是,如果DoSomething()
涉及x
变量的值更改,则差异在代码行为中绝对更重要。假设我们将 x 作为私有静态成员(初始值始终为 1)而不是局部变量,并且:
public void DoSomething()
{
++m_X;
}
在第一种方法中,即使由于第一次检查而在调用m_X
后假定值为 2 DoSomething()
,else 也会使方法退出并且DoSomethingElse()
永远不会被调用。在第二种方法中,两种方法都将被调用。
请注意,C# 中没有else if
构造。您的第一个代码示例与以下内容完全相同:
if (x == 1)
DoSomething();
else
{
if (x == 2)
DoSomethingElse();
}
由于 中只有一条语句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
语句的情况下,执行所有满足其条件的分支。
这里的主要区别是分支的执行导致后续条件变为真。例如:
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
,而在第二种情况下x == 3
。
当你这样编码时
// approach two
if (x == 1)
DoSomething();
if (x == 2)
DoSomethingElse();
每次条件检查。
但是当你这样编码时
if (x == 1)
DoSomething();
else if (x == 2)
DoSomethingElse();
如果第一个条件为真,则它不会检查下一个 else if 条件,从而减少不必要的编译。
使用else
语句时,只会运行其中一个分支(即第一个,满足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();
而当您不使用它时,它们中的每一个都可能会运行(取决于每个条件),即它们中的每一个都被一一估计:
// 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 那么你在第一种情况下只做一次检查,在第二种情况下你做 2 次检查,所以第一种情况更快。
如果 x 发生变化(在 Do Seomthing 和 DoSomethingElse 中),那么第一个语句将只执行一个语句。在第二个示例中,将检查每个语句(当然,除非编译器将其优化为跳转表以进行数字比较)。
当你使用 multipleelse if
时,它会执行满足的条件。如果还有剩余的情况,它们将被跳过。当您有多个if
时,它将检查每个语句。所以这更像是一个性能问题。
如果x
被多个线程修改,则可能DoSomething()
会DoSomethingElse()
使用第二种方法调用