简体   繁体   中英

The behavior of null-coalescing assignment operator

I just wonder that how ??= operator runs in the background. I have two questions about it.

Consider the following example,

string name = "John";
name ??= "George";

1) Is it equal to name = name ?? "George"; name = name ?? "George";

2) Does it work like this,

if (name == null) {
   name = "George";
}

or

if (name == null) {
   name = "George";
}
else {
   name = name;
}

It will be evaluated to this:

string text = "John";
if (text == null)
{
    text = "George";
}

You can use the sharplab to see what happens actually:

https://sharplab.io/#v2:CYLg1APgAgTAjAWAFBQMwAJboMLoN7LpGYZQAs6AsgBQCU+hxTUcADOgHYCGAtgKboAvOgBEAKQD2ACw4iA3Mm790AfhXCRAcT4SATgHM+8xkQC+yU0A

Further Info: https://stackoverflow.com/a/59300172/2946329

Based on the documentation :

C# 8.0 introduces the null-coalescing assignment operator ??=. You can use the ??= operator to assign the value of its right-hand operand to its left-hand operand only if the left-hand operand evaluates to null.

According to documentation

null-coalescing assignment operator ??= assigns the value of its right-hand operand to its left-hand operand only if the left-hand operand evaluates to null . The ??= operator doesn't evaluate its right-hand operand if the left-hand operand evaluates to non-null.

In you code sample it won't be evaluated, since name isn't null

string name = "John";
name ??= "George";

It will work if you write something like that

string name = null;
name ??= "George";

Name value will be George . Expanded variant is

if (name is null) //or name == null
{
    name = "George";
}

The null-coalescing operator ?? returns the value of left-hand operand if it isn't null ; otherwise, it evaluates the right-hand operand and returns result.

In this sample name = name ?? "George" name = name ?? "George" the result will be George only when name has null value before. I your sample name = name ?? "George"; name = name ?? "George"; is equal to name ??= "George"; in terms of return result. But in both cases you can get a George value only when original name is null before assigning. You can also refer to language specification for details

You can use https://sharplab.io/ to test for differences. The difference between ??= and ?? is very small, and actually disappears once the code is JIT-compiled.

In short :

  1. Once the code is compiled to assembly, it's the same.
  2. The equivalent is :
if (text == null){
    text = "George";
}

SharpLab Examples

The code for this example :

public void M1() {
    string name = "John";
     name ??= "George";
    Console.WriteLine(name);
}

public void M2() {
    string name = "John";
    name = name  ?? "George";
    Console.WriteLine(name);
}

Generates this intermediate C# code, that shows a real difference :

public void M1()
{
    string text = "John";
    if (text == null)
    {
        text = "George";
    }
    Console.WriteLine(text);
}

public void M2()
{
    string text = "John";
    text = (text ?? "George");
    Console.WriteLine(text);
}

The IL though is almost the same , except for a dup (copy) and pop operation. You'd think that ?? is somewhat slower for this :

    IL_0000: nop
    IL_0001: ldstr "John"
    IL_0006: stloc.0
    IL_0007: ldloc.0
    IL_0008: brtrue.s IL_0010
    IL_000a: ldstr "George"
    IL_000f: stloc.0
    IL_0010: ldloc.0
    IL_0011: call void [System.Console]System.Console::WriteLine(string)
    IL_0016: nop
    IL_0017: ret

vs

        IL_0000: nop
        IL_0001: ldstr "John"
        IL_0006: stloc.0
        IL_0007: ldloc.0
***     IL_0008: dup
        IL_0009: brtrue.s IL_0011
***     IL_000b: pop
        IL_000c: ldstr "George"
        IL_0011: stloc.0
        IL_0012: ldloc.0
        IL_0013: call void [System.Console]System.Console::WriteLine(string)
        IL_0018: nop
        IL_0019: ret

BUT the assembly in Release mode is identical:

C.M1()
    L0000: mov ecx, [0x1a58b46c]
    L0006: test ecx, ecx
    L0008: jnz L0010
    L000a: mov ecx, [0x1a58b470]
    L0010: call System.Console.WriteLine(System.String)
    L0015: ret

C.M2()
    L0000: mov ecx, [0x1a58b46c]
    L0006: test ecx, ecx
    L0008: jnz L0010
    L000a: mov ecx, [0x1a58b470]
    L0010: call System.Console.WriteLine(System.String)
    L0015: ret

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM