1. The old version of the code
Namespace CSHARP6
{
internal class person
{public
string Name {get; set;}
}
Internal class program
{
private static void Main ()
{person person
= null;
string name = NULL;
if (person!= null)
{
name = person. Name;}}}
When we use an object's properties, sometimes the first step is to determine whether the object itself is bull, or you may get a System.NullReferenceException exception. Although sometimes we can use the ternary operator string name = Person!= null? Person. Name:null to simplify the code, but it's not easy to write ... Because of the very common coding behavior in programming when null values are detected, SO,C#6 brings us a much simpler way.
2. Null condition operator
Namespace CSHARP6
{
internal class person
{public
string Name {get; set;}
}
Internal class program
{
private static void Main ()
{person person
= null;
String name = Person?. Name;}}
}
From the above we can see that use? This approach can be used instead of if judgments and simplifying the use of ternary operators, simple to no more concise. By convention, the two IL codes are compared.
Old version of IL code:
. method private Hidebysig static void Main () cil managed
{
. entrypoint
//Code size (0x17)
. Maxstack 2
Locals init ([0] class CSHARP6. person person,
[1] string name,
[2] bool v_2)
Il_0000:nop
il_0001:ldnull
il_0002:stloc.0
Il_0003:ldnull
Il_0004:stloc.1
il_0005:ldloc.0
il_0006:ldnull
IL_0007:cgt.un
il_0009:stloc.2
il_000a : Ldloc.2
il_000b:brfalse.s il_0016
il_000d:nop
il_000e:ldloc.0
il_000f:callvirt String CSHARP6. Person::get_name ()
il_0014:stloc.1
il_0015:nop
il_0016:ret
}//End of method Program::main
If version of IL
Il of the new syntax:
. method private Hidebysig static void Main () cil managed
{
. entrypoint
//Code size (0x11)
. Maxstack 1
Locals init ([0] class CSHARP6. person person,
[1] string name
il_0000:nop
il_0001:ldnull
il_0002:stloc.0
il_0003:ldloc.0< C12/>IL_0004:BRTRUE.S il_0009
il_0006:ldnull
il_0007:br.s il_000f
il_0009:ldloc.0
000a:call instance string csharp6. Person::get_name ()
il_000f:stloc.1
Il_0010:ret
}//End of method Program::main
Il for null conditional operator version
Gee, there seems to be a big difference, let's have another ternary operator version of the Il look:
. method private Hidebysig static void Main () cil managed
{
. entrypoint
//Code size (0x11)
. Maxstack 1
Locals init ([0] class CSHARP6. person person,
[1] string name
il_0000:nop
il_0001:ldnull
il_0002:stloc.0
il_0003:ldloc.0< C12/>IL_0004:BRTRUE.S il_0009
il_0006:ldnull
il_0007:br.s il_000f
il_0009:ldloc.0
000a:callvirt instance string csharp6. Person::get_name ()
il_000f:stloc.1
Il_0010:ret
}//End of method Program::main
Il of ternary operator edition
New syntax "?." and the ternary operator "?:" The result is that the only difference is il_000a this line. "?." The way is compiled into call, and the "?:" Way is compiled into callvirt, somehow "?:" in the persion. Why is name compiled into a callvirt that supports polymorphic invocation, in which case it seems more efficient to call, but ultimately "?." and "?:" compiled code is not fundamentally different.
But it's simpler to compare to if judgments, and we analyze the IL to see what the differences are (ignoring the difference between call and callvirt):
Il analysis of the IF version:
Method private hidebysig static void Main () cil managed {. entrypoint. maxstack 2. Locals init ([0] class CSHARP6. Person person,//initialize local variable person, place person at index 0 [1] string name,///Initialize local variable name, place name at index 1 [2] bool v_2) Initialize local variable v_2, place v_2 at index 2 IL_0000:NOP//null IL_0001:LDNULL//LOAD NULL il_0002:stloc.0//NULL
The variable in index 0, which is the person object.
Il_0003:ldnull//load NULL IL_0004:STLOC.1//drop NULL into a variable indexed to 1, which is the name object. il_0005:ldloc.0//Load the variable with index 0, that is, the person object il_0006:ldnull//Load null IL_0007:cgt.un//Compare the values loaded in the first two steps.
If the first value is greater than the second value, the integer value of 1 is pushed onto the evaluation stack, and the 0 is pushed onto the evaluation stack instead. IL_0009:STLOC.2//Put the comparison into a variable indexed to 2, that is, the V_2 object IL_000A:LDLOC.2//Loading an object with an index of 2, that is, V_2 object Il_000b:brfalse.s il_00
16//If the object loaded in the previous step is false, null reference, or 0, it jumps to the il_0016 position, which is the end of the current method. Il_000d:nop//Empty il_000e:ldloc.0//load the variable with index 0, which is the person object Il_000f:callvirt instance string csharp6.
Person::get_name ()//calls the Get_name method of the Person object. Il_0014:stloc.1 Save the results of the previous step in a variable indexed to 1, which is the name object. Il_0015:nop//Empty Il_0016:ret//return}
Il analysis of the null conditional operator version:
. method private Hidebysig static void Main () cil managed
{
. entrypoint
. maxstack 1
. Locals init ([0] Clas S CSHARP6. Person person,//initialize local variable person, place person at index 0
[1] String name //Initialize local variable name, place name at index 1 location
il_0000: NOP //null
il_0001:ldnull //load null
il_0002:stloc.0 //drop NULL into a variable indexed to 0, which is the person object
Il_ 0003:ldloc.0 //Load the variable with index 0, that is, the person object
il_0004:brtrue.s il_0009 //If the object loaded in the previous step is true, Non-null, or Non-zero , then jump to il_0009 position
il_0006:ldnull //load null
IL_0007:BR.S il_000f //unconditional jump to il_000f
IL _0009:ldloc.0 //Loads the variable at the position of index 0, which is the person object
Il_000a:call instance string csharp6. Person::get_name ()////the Get_name method that invokes the person object.
il_000f:stloc.1 //The result of the previous step is stored in a variable indexed to 1, which is the name object.
Il_0010:ret //Return
}
Through analysis we found that the null operator compiled IL code is shorter, using 2 branch jumps, simplifying the logic of judgment, and if version of Il more than a bool type of v_2 temporary variables.
So, the conclusion is "?." and ternary operator "?:" The compilation result is the same, and simplifies the judgment of If. So whether it's about performance or readability, "?." is the recommended wording.
3. Example 3.1? [
The null condition operator can use not only the?. Syntax to access the properties and methods of the object. The syntax accesses whether an object that detects an array or contains an indexer is null. Like what:
person[] persons = NULL;
//?.
Int? Length = persons? Length;
//? [Person I
= persons? [0];
3.2?. Combine??
The persions above? The result returned by lenght is nullable type, and sometimes we may need an int type, then we can combine the null join operator "??" Use together, for example:
person[] persons = NULL;
//?. And?? Combined use
int length = persons?. Length?? 0;
3.3 Calling events in a thread-safe manner
PropertyChangedEventHandler propertychanged = propertychanged;
if (propertychanged!= null)
{
propertychanged (this, new PropertyChangedEventArgs (Nameof (Name));
}
The above code is always the way we call events, and the reference to the event is placed in a temporary variable to prevent the event from being unregistered when the delegate is invoked, resulting in null.
We can finally invoke the event in a simpler way after c#6 (this ridge has continued since the C#1 era ...). ):
PropertyChanged? Invoke (PropertyChanged (This, new PropertyChangedEventArgs (Nameof (Name));
4. Summary
The null conditional operator is a syntax simplification, and it does a compilation optimization, and the optimization is consistent with the optimization of the ternary operator. The syntax is simpler and the performance is better, so why don't we use the new syntax?