“else if()” versus multiple “if()”s in C#

后端 未结 10 2065
一个人的身影
一个人的身影 2021-01-03 21:51

How do these practically differ?

// Approach one
if (x == 1)
    DoSomething();
else if (x == 2)
    DoSomethingElse();

// Approach two
if (x == 1)
    DoSo         


        
10条回答
  •  谎友^
    谎友^ (楼主)
    2021-01-03 22:54

    [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.

提交回复
热议问题