Search code examples
c#.netvariablescompiler-construction

In C#, why assign member variables to local variables and then use local variables instead of using member variables directly


In C#, why assign member variables to local variables and then use local variables instead of using member variables directly, as shown in the following snippet:


internal class LocalVariableTest
{
    private readonly int[] _items = new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };


    public void ConsoleWriteLine()
    {
        var items = _items;

        foreach (var item in items)
        {
            System.Console.WriteLine(item);
        }
    }

    public void ConsoleWriteLine2()
    {
        foreach (var item in _items)
        {
            System.Console.WriteLine(item);
        }
    }
}

What is the difference between ConsoleWriteLine and ConsoleWriteLine2?

I would like to know if there is any difference between the two methods after compiling. I have seen many places that have this way of writing. Which one has higher performance and where is the knowledge about this, such as Microsoft Learn?


Solution

  • Any difference? barely.

    The compiler should optimize it to be approximately the same.

    Here, the difference in debug mode is two extra instructions and an additional variable on the stack. Will that be noticeable? Only way to know for sure is to run a benchmark with something like benchmark.net.

    As pointed out in the comments, your code in release mode is identical.

    You can view the difference with ILSpy, or an online equivalent like sharplab

    // Methods
        .method public hidebysig 
            instance void ConsoleWriteLine () cil managed 
        {
            // Method begins at RVA 0x2094
            // Code size 39 (0x27)
            .maxstack 2
            .locals init (
                [0] int32[] items,
                [1] int32[],
                [2] int32,
                [3] int32 item
            )
    
            IL_0000: nop
            IL_0001: ldarg.0
            IL_0002: ldfld int32[] LocalVariableTest::_items
            IL_0007: stloc.0
            IL_0008: nop
            IL_0009: ldloc.0
            IL_000a: stloc.1
            IL_000b: ldc.i4.0
            IL_000c: stloc.2
            // sequence point: hidden
            IL_000d: br.s IL_0020
            // loop start (head: IL_0020)
                IL_000f: ldloc.1
                IL_0010: ldloc.2
                IL_0011: ldelem.i4
                IL_0012: stloc.3
                IL_0013: nop
                IL_0014: ldloc.3
                IL_0015: call void [System.Console]System.Console::WriteLine(int32)
                IL_001a: nop
                IL_001b: nop
                // sequence point: hidden
                IL_001c: ldloc.2
                IL_001d: ldc.i4.1
                IL_001e: add
                IL_001f: stloc.2
    
                IL_0020: ldloc.2
                IL_0021: ldloc.1
                IL_0022: ldlen
                IL_0023: conv.i4
                IL_0024: blt.s IL_000f
            // end loop
    
            IL_0026: ret
        } // end of method LocalVariableTest::ConsoleWriteLine
    
        .method public hidebysig 
            instance void ConsoleWriteLine2 () cil managed 
        {
            // Method begins at RVA 0x20c8
            // Code size 37 (0x25)
            .maxstack 2
            .locals init (
                [0] int32[],
                [1] int32,
                [2] int32 item
            )
    
            IL_0000: nop
            IL_0001: nop
            IL_0002: ldarg.0
            IL_0003: ldfld int32[] LocalVariableTest::_items
            IL_0008: stloc.0
            IL_0009: ldc.i4.0
            IL_000a: stloc.1
            // sequence point: hidden
            IL_000b: br.s IL_001e
            // loop start (head: IL_001e)
                IL_000d: ldloc.0
                IL_000e: ldloc.1
                IL_000f: ldelem.i4
                IL_0010: stloc.2
                IL_0011: nop
                IL_0012: ldloc.2
                IL_0013: call void [System.Console]System.Console::WriteLine(int32)
                IL_0018: nop
                IL_0019: nop
                // sequence point: hidden
                IL_001a: ldloc.1
                IL_001b: ldc.i4.1
                IL_001c: add
                IL_001d: stloc.1
    
                IL_001e: ldloc.1
                IL_001f: ldloc.0
                IL_0020: ldlen
                IL_0021: conv.i4
                IL_0022: blt.s IL_000d
            // end loop
    
            IL_0024: ret
        } // end of method LocalVariableTest::ConsoleWriteLine2