Поведение нуль-коалесцирующего оператора присваивания - PullRequest
1 голос
/ 11 февраля 2020

Мне просто интересно, как работает ?? = оператор в фоновом режиме. У меня есть два вопроса по этому поводу.

Рассмотрим следующий пример:

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

1) Это равно name = name ?? "George";

2) Работает ли это так,

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

или

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

Ответы [ 3 ]

7 голосов
/ 11 февраля 2020

Это будет оцениваться на это:

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

Вы можете использовать sharplab, чтобы посмотреть, что происходит на самом деле:

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

Дополнительная информация: { ссылка }

На основе документации :

C# 8.0 представляет оператор присваивания с нулевым слиянием ?? =. Вы можете использовать оператор ?? =, чтобы присвоить значение своего правого операнда его левому операнду, только если левый операнд оценивается как ноль.

2 голосов
/ 11 февраля 2020

В соответствии с документацией

нуль-сливающийся оператор присваивания ??= присваивает значение своего правого операнда своему левому операнду, только если левый операнд оценивается как null. Оператор ??= не оценивает свой правый операнд, если левый операнд оценивается как ненулевой.

В вашем примере кода он не будет оцениваться, поскольку name не null

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

Это будет работать, если вы напишите что-то подобное

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

Значение имени будет George. Расширенный вариант:

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

Нулевой оператор ?? возвращает значение левого операнда, если оно не null; в противном случае он оценивает правый операнд и возвращает результат.

В этом примере name = name ?? "George" результат будет George только тогда, когда name имеет значение null ранее. I Ваш образец name = name ?? "George"; равен name ??= "George"; с точки зрения возвращаемого результата. Но в обоих случаях вы можете получить значение George только тогда, когда оригинал name равен null до назначения. Вы также можете обратиться к спецификации языка для деталей

0 голосов
/ 11 февраля 2020

Вы можете использовать https://sharplab.io/ для проверки на различия. Разница между ??= и ?? очень мала, и на самом деле исчезает после того, как код скомпилирован JIT.

Короче:

  1. Один раз код скомпилирован в сборку, это то же самое.
  2. Эквивалент:
if (text == null){
    text = "George";
}

Примеры SharpLab

Код этот пример :

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

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

Генерирует этот промежуточный код C#, который показывает реальную разницу:

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);
}

Хотя значение IL почти такое же , за исключением dup (копия) и pop операция. Можно подумать, что ?? несколько медленнее для этого:

    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

против

        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

НО сборка в режиме выпуска идентична:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...