Ответ 1
Я воспроизвел его в VS2012 со следующим кодом:
public void Test()
{
TestFoo();
}
private Foo _foo;
private void TestFoo(Foo foo = null)
{
_foo = foo ?? new Foo();
}
public class Foo
{
}
Если вы установите точку останова в конце метода TestFoo
, вы ожидаете увидеть набор переменных _foo
, но он все равно будет отображаться как нулевой в отладчике.
Но если вы затем сделаете что-нибудь с _foo
, оно появится правильно. Даже простое назначение, например
_foo = foo ?? new Foo();
var f = _foo;
Если вы пройдете через него, вы увидите, что _foo
показывает значение null, пока не будет присвоено значение f
.
Это напоминает мне отложенное поведение выполнения, например, с LINQ, но я не могу найти ничего, что бы подтвердить это.
Совершенно возможно, что это просто причуда отладчика. Возможно, кто-то с навыками MSIL может пролить свет на то, что происходит под капотом.
Также интересно, что если вы замените нулевой оператор коалесцирования эквивалентом:
_foo = foo != null ? foo : new Foo();
Тогда он не проявляет этого поведения.
Я не парень сборки /MSIL, но просто взглянуть на результат разборки между двумя версиями интересно:
_foo = foo ?? new Foo();
0000002d mov rax,qword ptr [rsp+68h]
00000032 mov qword ptr [rsp+28h],rax
00000037 mov rax,qword ptr [rsp+60h]
0000003c mov qword ptr [rsp+30h],rax
00000041 cmp qword ptr [rsp+68h],0
00000047 jne 0000000000000078
00000049 lea rcx,[FFFE23B8h]
00000050 call 000000005F2E8220
var f = _foo;
00000055 mov qword ptr [rsp+38h],rax
0000005a mov rax,qword ptr [rsp+38h]
0000005f mov qword ptr [rsp+40h],rax
00000064 mov rcx,qword ptr [rsp+40h]
00000069 call FFFFFFFFFFFCA000
0000006e mov r11,qword ptr [rsp+40h]
00000073 mov qword ptr [rsp+28h],r11
00000078 mov rcx,qword ptr [rsp+30h]
0000007d add rcx,8
00000081 mov rdx,qword ptr [rsp+28h]
00000086 call 000000005F2E72A0
0000008b mov rax,qword ptr [rsp+60h]
00000090 mov rax,qword ptr [rax+8]
00000094 mov qword ptr [rsp+20h],rax
Сравните это с версией inlined-if:
_foo = foo != null ? foo : new Foo();
0000002d mov rax,qword ptr [rsp+50h]
00000032 mov qword ptr [rsp+28h],rax
00000037 cmp qword ptr [rsp+58h],0
0000003d jne 0000000000000066
0000003f lea rcx,[FFFE23B8h]
00000046 call 000000005F2E8220
0000004b mov qword ptr [rsp+30h],rax
00000050 mov rax,qword ptr [rsp+30h]
00000055 mov qword ptr [rsp+38h],rax
0000005a mov rcx,qword ptr [rsp+38h]
0000005f call FFFFFFFFFFFCA000
00000064 jmp 0000000000000070
00000066 mov rax,qword ptr [rsp+58h]
0000006b mov qword ptr [rsp+38h],rax
00000070 nop
00000071 mov rcx,qword ptr [rsp+28h]
00000076 add rcx,8
0000007a mov rdx,qword ptr [rsp+38h]
0000007f call 000000005F2E72A0
var f = _foo;
00000084 mov rax,qword ptr [rsp+50h]
00000089 mov rax,qword ptr [rax+8]
0000008d mov qword ptr [rsp+20h],rax
Исходя из этого, я думаю, что происходит какое-то отсроченное исполнение. Оператор присваивания во втором примере очень мал по сравнению с первым примером.