This is the nineteenth part of the .NET Inside Out series. For your convenience you can find other parts in the table of contents in Part 1 – Virtual and non-virtual calls in C#
Today we will see that .NET can create an instance of a structure without calling its constructor if it is not needed.
Let’s take this code:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public struct SomeStructure {     public int Value { get; set; }     public SomeStructure(int value)     {         Value = value;     }     public int Foo()     {         return new SomeStructure(123).Value;     }     public int Bar()     {         var s = new SomeStructure(456);         return s.Value;     } } | 
Structure with integer field. Then, two methods creating a structure and returning its field. The difference is first method returns directly, second method stores in a variable. Let’s see IL:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | .method public hidebysig  	instance int32 Foo () cil managed  { 	// Method begins at RVA 0x206c 	// Code size 16 (0x10) 	.maxstack 1 	.locals init ( 		[0] valuetype SomeStructure 	) 	IL_0000: ldc.i4.s 123 	IL_0002: newobj instance void SomeStructure::.ctor(int32) 	IL_0007: stloc.0 	IL_0008: ldloca.s 0 	IL_000a: call instance int32 SomeStructure::get_Value() 	IL_000f: ret } // end of method SomeStructure::Foo | 
We can see that we create an instance using newobj. Second method:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | .method public hidebysig  	instance int32 Bar () cil managed  { 	// Method begins at RVA 0x2088 	// Code size 20 (0x14) 	.maxstack 2 	.locals init ( 		[0] valuetype SomeStructure 	) 	IL_0000: ldloca.s 0 	IL_0002: ldc.i4 456 	IL_0007: call instance void SomeStructure::.ctor(int32) 	IL_000c: ldloca.s 0 	IL_000e: call instance int32 SomeStructure::get_Value() 	IL_0013: ret } // end of method SomeStructure::Bar | 
And magic, this time we don’t create an instance, we just call the constructor. This is optimization on the IL level. If we check jitted code in debug mode:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | SomeStructure.Foo()     L0000: push ebp     L0001: mov ebp, esp     L0003: sub esp, 0x14     L0006: xor eax, eax     L0008: mov [ebp-0x8], eax     L000b: mov [ebp-0xc], eax     L000e: mov [ebp-0x4], ecx     L0011: cmp dword [0x13ebc1a8], 0x0     L0018: jz L001f     L001a: call 0x726310f0     L001f: nop     L0020: xor ecx, ecx     L0022: mov [ebp-0x10], ecx     L0025: lea ecx, [ebp-0x10]     L0028: mov edx, 0x7b     L002d: call SomeStructure..ctor(Int32)     L0032: mov ecx, [ebp-0x10]     L0035: mov [ebp-0x8], ecx     L0038: lea ecx, [ebp-0x8]     L003b: call SomeStructure.get_Value()     L0040: mov [ebp-0x14], eax     L0043: mov eax, [ebp-0x14]     L0046: mov [ebp-0xc], eax     L0049: nop     L004a: jmp L004c     L004c: mov eax, [ebp-0xc]     L004f: mov esp, ebp     L0051: pop ebp     L0052: ret | 
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | SomeStructure.Bar()     L0000: push ebp     L0001: mov ebp, esp     L0003: sub esp, 0x10     L0006: xor eax, eax     L0008: mov [ebp-0x8], eax     L000b: mov [ebp-0xc], eax     L000e: mov [ebp-0x4], ecx     L0011: cmp dword [0x13ebc1a8], 0x0     L0018: jz L001f     L001a: call 0x726310f0     L001f: nop     L0020: lea ecx, [ebp-0x8]     L0023: mov edx, 0x1c8     L0028: call SomeStructure..ctor(Int32)     L002d: lea ecx, [ebp-0x8]     L0030: call SomeStructure.get_Value()     L0035: mov [ebp-0x10], eax     L0038: mov eax, [ebp-0x10]     L003b: mov [ebp-0xc], eax     L003e: nop     L003f: jmp L0041     L0041: mov eax, [ebp-0xc]     L0044: mov esp, ebp     L0046: pop ebp     L0047: ret | 
We can see there are differences (reflecting differences in IL). However, in Release:
| 1 2 3 4 5 6 7 | SomeStructure.Foo()     L0000: mov eax, 0x7b     L0005: ret SomeStructure.Bar()     L0000: mov eax, 0x1c8     L0005: ret | 
It works in the same way.