How do these practically differ?
// Approach one
if (x == 1)
DoSomething();
else if (x == 2)
DoSomethingElse();
// Approach two
if (x == 1)
DoSo
When you use multiple else if
, it will execute the condition that meets. If there are remaining cases, they will be skipped. When you have multiple if
, it will check every statement. So this becomes more of a performance issue.
No anwsers about performance?
So if x=1 then you do only one check in first case, and in second case you do 2 checks, so first case is faster.
When you use else
statement, only one of the branches will be run (i.e. the first, which meets the if
condition). All other if
conditions won't even be estimated:
// 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();
While when you don't use it each of them may be run (depending on each of the conditions), i.e. each of them is estimated one by one:
// 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
So, IL may differ.
[STAThread]
public static void Main()
{
Int32 x = 1;
if (x == 1)
Console.WriteLine("1");
else if (x == 2)
Console.WriteLine("2");
}
Results in:
.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
}
While:
[STAThread]
public static void Main()
{
Int32 x = 1;
if (x == 1)
Console.WriteLine("1");
if (x == 2)
Console.WriteLine("2");
}
Results in:
.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 code is a little bit different and here is the main difference:
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
When you use else statement, as in first approach, only the first branch that meets the condition will be run. On the other hand... with the second approach every check is processed and every check that meets the condition will be followed and processed. That's the main difference.
That's why in the first approach's IL code you have a "ret" directive just after the call of Console.WriteLine while in the second it's not present. In the first case the method can be exited just after a check has been passed because no more checks on x
will be performed... in the second approach you have to follow all of them sequentially and that's why ret is only appearing at the end of the method, no "shortcuts" to the end.
For my test i used a Console.WriteLine()
call... but it's sure that if DoSomething()
involves a value change of x
variable, the difference will be absolutely more important in the code behavior. Let's say that we have x as a private static member (initial value always 1) instead of a local variable and:
public void DoSomething()
{
++m_X;
}
In the first approach, even if m_X
assumes a value of 2 after DoSomething()
is called thanks to the first check, else will make the method exit and DoSomethingElse()
will never be called. In the second approach both methods will be called.