Is the Null-Conditional operator optimized on consecutive usage sites or does it lead to double checks?

Does the null-conditional operator use null checking duplication? for example

var x = instance?.Property1; var y = instance?.Property2; 

Is this compiled:

 if (instance != null) { var x = instance.Property1; var y = instance.Property2; } 

Or that?

 if (instance != null) { var x = instance.Property1; } if (instance != null) { var y = instance.Property2; } 

If the first, does the difference between whether the other code exist between the two lines matter? In other words, how smart is the compiler / optimizer?

+8
source share
3 answers

The compiler seems completely unaware of this.

The code:

 var x = instance?.Property1; var y = instance?.Property2; 

... compiles as not optimized for:

 IL_0000: nop IL_0001: newobj UserQuery+Class..ctor IL_0006: stloc.0 // instance IL_0007: ldloc.0 // instance IL_0008: brtrue.s IL_000D IL_000A: ldnull IL_000B: br.s IL_0013 IL_000D: ldloc.0 // instance IL_000E: ldfld UserQuery+Class.Property1 IL_0013: stloc.1 // x IL_0014: ldloc.0 // instance IL_0015: brtrue.s IL_001A IL_0017: ldnull IL_0018: br.s IL_0020 IL_001A: ldloc.0 // instance IL_001B: ldfld UserQuery+Class.Property2 IL_0020: stloc.2 // y IL_0021: ret Class..ctor: IL_0000: ldarg.0 IL_0001: call System.Object..ctor IL_0006: nop IL_0007: ret 

... and optimized as:

 IL_0000: newobj UserQuery+Class..ctor IL_0005: dup IL_0006: dup IL_0007: brtrue.s IL_000C IL_0009: pop IL_000A: br.s IL_0012 IL_000C: ldfld UserQuery+Class.Property1 IL_0011: pop IL_0012: dup IL_0013: brtrue.s IL_0017 IL_0015: pop IL_0016: ret IL_0017: ldfld UserQuery+Class.Property2 IL_001C: pop IL_001D: ret Class..ctor: IL_0000: ldarg.0 IL_0001: call System.Object..ctor IL_0006: ret 

Both are explicitly with two branch checks.

+6
source share

It performs two separate If..Else checks for assignment using the null conditional statement operator in your case. Below is a disassembled code example

Source:

 public class nulltest { public void test() { var instance = new testclass(); var x = instance?.prop1; var y = instance?.prop2; } } public class testclass { public int prop1; public int prop2; } 

Disassembled Code (ILSpy):

 public class nulltest { public void test() { testclass testclass = new testclass(); if (testclass == null) { int? arg_20_0 = null; } else { new int?(testclass.prop1); } if (testclass == null) { int? arg_3A_0 = null; } else { new int?(testclass.prop2); } } } 

I used int as a property type, but the above should be true for any other case.

+5
source share

The compiler is strict, think about this code.

 class Tricky { public int Property1 { get { Program.instance = null; return 1; } } public int Property2 { get { return 2; } } } class Program { public static Tricky instance = new Tricky(); public static void Main(string[] arg) { var x = instance?.Property1; var y = instance?.Property2; //what do you think the values of x,y } } 

Expected Result: x == 1, y is null . But if the compiler optimizes the code using a single if , it throws a NullReferenceException . This means that using one if not a smart optimization, it is not an optimization, because it is WRONG.

+5
source share

All Articles