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?
CodePudding user response:
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