[C #6] 3-null conditional operator,
0. Directory
C #6 Add feature catalog
1. Old Version code
1 namespace csharp6 2 {3 internal class Person 4 {5 public string Name {get; set;} 6} 7 8 internal class Program 9 {10 private static void Main () 11 {12 Person person = null; 13 // if, 14 string name = null; 15 if (person! = Null) 16 {17 name = person. Name; 18} 19} 20} 21}
When we use the attributes of an object, sometimes the first step is to determine whether the object itself is bull. Otherwise, you may get an exception of System. NullReferenceException. Although sometimes we can use the ternary operator string name = person! = Null? Person. name: null; to simplify the code, but this writing method is not simple enough ...... due to a very common encoding behavior in programming during null value detection, so and C #6 provide us with a simpler method.
2. null condition Operator
1 namespace csharp6 2 { 3 internal class Person 4 { 5 public string Name { get; set; } 6 } 7 8 internal class Program 9 {10 private static void Main()11 {12 Person person = null;13 string name = person?.Name;14 }15 }16 }
We can see from the above that using ?. This method can replace if to judge and simplify the use of the ternary operators. It is so concise that it cannot be more concise. By convention, compare the last two pieces of IL code.
The old version of IL code:
1. method private hidebysig static void Main () cel managed 2 {3. entrypoint 4 // Code size 23 (0x17) 5. maxstack 2 6. locals init ([0] class csharp6.Person person, 7 [1] string name, 8 [2] bool V_2) 9 IL_0000: nop10 IL_0001: ldnull11 IL_0002: stloc.012 IL_0003: ldnull13 IL_0004: stloc.114 IL_0005: ldloc.015 IL_0006: ldnull16 IL_0007: cgt. un17 IL_0009: stloc.218 IL_000a: ldloc.219 IL_000b: brfalse. s queue IL_000d: nop2il_000e: queue IL_000f: callvirt instance string csharp6.Person: get_Name () 23 IL_0014: queue IL_0015: nop25 IL_0016: ret26} // end of method Program: MainIf version of IL
New syntax IL:
1. method private hidebysig static void Main () cel managed 2 {3. entrypoint 4 // Code size 17 (0x11) 5. maxstack 1 6. locals init ([0] class csharp6.Person person, 7 [1] string name) 8 IL_0000: nop 9 IL_0001: ldnull10 IL_0002: stloc.011 IL_0003: ldloc.012 IL_0004: brtrue. s IL_000913 IL_0006: ldnull14 IL_0007: br. s IL_000f15 IL_0009: ldloc.016 IL_000a: call instance string csharp6.Person: get_Name () 17 IL_000f: stloc.118 IL_0010: ret19} // end of method Program: MainNull condition operator version of IL
Struct, which seems to be quite different. Let's take a look at the ternary operator version of IL:
1. method private hidebysig static void Main () cel managed 2 {3. entrypoint 4 // Code size 17 (0x11) 5. maxstack 1 6. locals init ([0] class csharp6.Person person, 7 [1] string name) 8 IL_0000: nop 9 IL_0001: ldnull10 IL_0002: stloc.011 IL_0003: ldloc.012 IL_0004: brtrue. s IL_000913 IL_0006: ldnull14 IL_0007: br. s IL_000f15 IL_0009: ldloc.016 IL_000a: callvirt instance string csharp6.Person: get_Name () 17 IL_000f: stloc.118 IL_0010: ret19} // end of method Program: MainTernary operator version of IL
New Syntax "?. "And ternary operators "? : "The only difference between the results is IL_000a. "?. "Method is compiled into call, while "? : "The method is compiled as callvirt. I don't know why "? : "Why is the persion. Name in compiled into callvirt that supports multi-state calling? In this case, the call efficiency may be higher, but after all "?. "And "? : "The compiled code has no essential difference.
However, compared with the if judgment, this is simplified. Let's analyze the IL and see what are the differences (the difference between call and callvirt is ignored here ):
If version of IL analysis:
1. method private hidebysig static void Main () cel managed 2 {3. entrypoint 4. maxstack 2 5. locals init ([0] class csharp6.Person person, // initialize the local variable person, put the person at the position of 0, 6 [1] string name, // initialize the local variable name, place name in the position of Index 1 7 [2] bool V_2) // initialize the local variable V_2 and put V_2 in the position of index 2 8 IL_0000: nop/null 9 IL_0001: ldnull // load null10 IL_0002: stloc.0 // put null into the variable with the index 0, that is, the person object. 11 IL_0003: ldnull // load null12 IL_0004: stloc.1 // put null into the variable with index 1, that is, the name object. 13 IL_0005: ldloc.0 // load the variable at the position where the index is 0, that is, the person object 14 IL_0006: ldnull // load null15 IL_0007: cgt. un // compare the values loaded in the previous two steps. If the first value is greater than the second value, integer 1 is pushed to the computing stack. Otherwise, 0 is pushed to the computing stack. 16 IL_0009: stloc.2 // put the comparison result into the variable with index 2, that is, the V_2 object 17 IL_000a: ldloc.2 // load the object with index 2, that is, the V_2 object 18 IL_000b: brfalse. s IL_0016 // if the object to be loaded in the previous step is false, null reference, or zero, it will jump to the IL_0016 position, that is, end the current method. 19 IL_000d: nop // empty 20 IL_000e: ldloc.0 // load the variable at the position where the index is 0, that is, the person object 21 IL_000f: callvirt instance string csharp6.Person: get_Name () // call the get_Name method of the person object. 22 IL_0014: stloc.1 // Save the result of the previous step to the variable with index 1, that is, the name object. 23 IL_0015: nop // empty 24 IL_0016: ret // return 25}
IL Analysis for null condition operators:
1. method private hidebysig static void Main () cel managed 2 {3. entrypoint 4. maxstack 1 5. locals init ([0] class csharp6.Person person, // initialize the local variable person and put the person at the position of 0 [1] string name) // initialize the local variable name, put name in the position of Index 1 7 IL_0000: nop // null 8 IL_0001: ldnull // load null 9 IL_0002: stloc.0 // put null into the variable with index 0, that is, the person object 10 IL_0003: ldloc.0 // load the variable at the position where the index is 0, that is, the person object 11 IL_0004: brtrue. s IL_0009 // if the object loaded in the previous step Is true, non-null reference, or non-zero, jump to the IL_0009 Location 12 IL_0006: ldnull // load null13 IL_0007: br. s IL_000f // unconditionally jump to 14 IL_0009: ldloc.0 at IL_000f // load the variable at the position where the index is 0, that is, the person object 15 IL_000a: call instance string csharp6.Person: get_Name () //// call the get_Name method of the person object. 16 IL_000f: stloc.1 // Save the result of the previous step to the variable with index 1, that is, the name object. 17 IL_0010: ret // return 18}
Through analysis, we found that the IL Code Compiled by the null operator is shorter, and two branch jumps are used to simplify the judgment logic, the if version of IL also has a bool type of V_2 temporary variable.
So, the conclusion is "?. "And ternary operators "? : "The compilation results are the same, and the if judgment is simplified. Therefore, in terms of performance and readability ,"?. "Is recommended.
3. Example3.1? [
The null condition operator can not only use ?. Can I still use? [Syntax access check whether the array or the object containing the indexer is null. For example:
1 Person[] persons = null;2 //?.3 int? length = persons?.Length;4 //?[5 Person first = persons?[0];
3.2 ?. Combined ??
The above persions ?. The result returned by Lenght is of the Nullable <int> type. Sometimes we may need an int type, so we can combine the null join operator "?? "Used together, for example:
1 Person [] persons = null; 2 //?. And ?? Use 3 int length = persons ?. Length ?? 0;
3.3 call events in a thread-safe manner
1 PropertyChangedEventHandler propertyChanged = PropertyChanged;2 if (propertyChanged != null)3 {4 propertyChanged(this, new PropertyChangedEventArgs(nameof(Name)));5 }
The code above has always been the method for us to call the event processing. We put the event reference in a temporary variable to prevent the event from being deregistered when calling this delegate, null is generated.
We have finally been able to trigger event calls in a simpler way since C #6 (this pipeline has been in the C #1 era until now ...):
1 PropertyChanged?.Invoke(propertyChanged(this, new PropertyChangedEventArgs(nameof(Name)));
4. Summary
The null condition operator is a simplified syntax and compilation optimization. The optimization method is consistent with that of the ternary operator. The syntax is simplified and the performance is better. What is the reason for us not to use the new syntax.
5. Reference
C #-Reference-Operators: Null-conditional Operators