La respuesta de Douglas es correcta sobre el código muerto de optimización de JIT ( tanto los compiladores x86 como los x64 harán esto). Sin embargo, si el compilador JIT estuviera optimizando el código muerto, sería inmediatamente obvio porquex ni siquiera aparecería en la ventana Locales. Además, el reloj y la ventana inmediata le darían un error al intentar acceder: "El nombre 'x' no existe en el contexto actual". Eso no es lo que ha descrito que está sucediendo.
Lo que está viendo es en realidad un error en Visual Studio 2010.
Primero, intenté reproducir este problema en mi máquina principal: Win7x64 y VS2012. Para los objetivos .NET 4.0, xes igual a 3.0D cuando se rompe en la llave de cierre. Decidí probar también los destinos .NET 3.5, y con eso,x también se configuró en 3.0D, no nulo.
Como no puedo hacer una reproducción perfecta de este problema ya que tengo .NET 4.5 instalado sobre .NET 4.0, puse en marcha una máquina virtual e instalé VS2010 en ella.
Aquí, pude reproducir el problema. Con un punto de interrupción en el corchete de cierre del Mainmétodo, tanto en la ventana del reloj como en la ventana de locales, vi quex era null. Aquí es donde comienza a ponerse interesante. En su lugar, apunté al tiempo de ejecución v2.0 y descubrí que allí también era nulo. Seguramente ese no puede ser el caso ya que tengo la misma versión del tiempo de ejecución de .NET 2.0 en mi otra computadora que se mostró exitosamente xcon un valor de 3.0D.
Entonces, ¿qué está pasando entonces? Después de investigar un poco en windbg, encontré el problema:
VS2010 le muestra el valor de x antes de que se le haya asignado .
Sé que no es así, ya que el puntero de instrucción está más allá de la x = y + zlínea. Puede probar esto usted mismo agregando algunas líneas de código al método:
double? y = 1D;
double? z = 2D;
double? x;
x = y + z;
Console.WriteLine();
Con un punto de interrupción en la llave final, los lugareños y la ventana de observación se muestran xcomo iguales a 3.0D. Sin embargo, si paso a través del código, usted notará que VS2010 no muestra xcomo asignados hasta después de que hubiera entrado a través de la Console.WriteLine().
No sé si este error se informó alguna vez a Microsoft Connect, pero es posible que desee hacerlo, con este código como ejemplo. Sin embargo, claramente se ha solucionado en VS2012, por lo que no estoy seguro de si habrá una actualización para solucionarlo o no.
Esto es lo que está sucediendo realmente en el JIT y el VS2010
Con el código original, podemos ver qué está haciendo VS y por qué está mal. También podemos ver que elx variable no se está optimizando (a menos que haya marcado el ensamblado para que se compile con las optimizaciones habilitadas).
Primero, veamos las definiciones de variables locales del IL:
.locals init (
[0] valuetype [mscorlib]System.Nullable`1<float64> y,
[1] valuetype [mscorlib]System.Nullable`1<float64> z,
[2] valuetype [mscorlib]System.Nullable`1<float64> x,
[3] valuetype [mscorlib]System.Nullable`1<float64> CS$0$0000,
[4] valuetype [mscorlib]System.Nullable`1<float64> CS$0$0001,
[5] valuetype [mscorlib]System.Nullable`1<float64> CS$0$0002)
Esta es una salida normal en modo de depuración. Visual Studio define variables locales duplicadas que se utilizan durante las asignaciones y luego agrega comandos IL adicionales para copiarlo de la variable CS * a su respectiva variable local definida por el usuario. Aquí está el código IL correspondiente que muestra que esto sucede:
L_0045: ldloca.s CS$0$0000
L_0047: call instance !0 [mscorlib]System.Nullable`1<float64>::GetValueOrDefault()
L_004c: conv.r8
L_004d: ldloca.s CS$0$0001
L_004f: call instance !0 [mscorlib]System.Nullable`1<float64>::GetValueOrDefault()
L_0054: conv.r8
L_0055: add
L_0056: newobj instance void [mscorlib]System.Nullable`1<float64>::.ctor(!0)
L_005b: nop
L_005c: stloc.2
L_005d: ret
Hagamos una depuración más profunda con WinDbg:
Si depura la aplicación en VS2010 y deja un punto de interrupción al final del método, podemos adjuntar WinDbg fácilmente, en modo no invasivo.
Aquí está el marco para el Main método en la pila de llamadas. Nos preocupamos por la IP (puntero de instrucción).
0: 009>! Clrstack
ID del hilo del SO: 0x135c (9)
Sitio de llamada IP de SP secundario
000000001c48dc00 000007ff0017338d ConsoleApplication1.Program.Main (System.String [])
[Y así...]
Si vemos el código de máquina nativo del Mainmétodo, podemos ver qué instrucciones se han ejecutado en el momento en que VS interrumpe la ejecución:
000007ff`00173388 e813fe25f2 llamar mscorlib_ni + 0xd431a0
(000007fe`f23d31a0) (System.Nullable`1 [[System.Double, mscorlib]] .. ctor (Double), mdToken: 0000000006001ef2)
**** 000007ff`0017338d cc int 3 ****
000007ff`0017338e 8d8c2490000000 lea ecx, [rsp + 90h]
000007ff`00173395 488b01 mov rax, qword ptr [rcx]
000007ff`00173398 4889842480000000 mov qword ptr [rsp + 80h], rax
000007ff`001733a0 488b4108 mov rax, qword ptr [rcx + 8]
000007ff`001733a4 4889842488000000 mov qword ptr [rsp + 88h], rax
000007ff`001733ac 488d8c2480000000 lea rcx, [rsp + 80h]
000007ff`001733b4 488b01 mov rax, qword ptr [rcx]
000007ff`001733b7 4889442440 mov qword ptr [rsp + 40h], rax
000007ff`001733bc 488b4108 mov rax, qword ptr [rcx + 8]
000007ff`001733c0 4889442448 mov qword ptr [rsp + 48h], rax
000007ff`001733c5 eb00 jmp 000007ff`001733c7
000007ff`001733c7 0f28b424c0000000 movaps xmm6, xmmword ptr [rsp + 0C0h]
000007ff`001733cf 4881c4d8000000 agregar rsp, 0D8h
000007ff`001733d6 c3 ret
El uso de la IP actual que nos dieron de !clrstacken Main, vemos que la ejecución fue suspendida en la instrucción inmediatamente después de la llamada al System.Nullable<double>constructor 's. ( int 3es la interrupción utilizada por los depuradores para detener la ejecución) He rodeado esa línea con *, y también puede hacer coincidir la línea L_0056en el IL.
El ensamblado x64 que sigue en realidad lo asigna a la variable local x. Nuestro puntero de instrucción aún no ha ejecutado ese código, por lo que VS2010 se rompe prematuramente antes de que el xcódigo nativo asigne la variable.
EDITAR: En x64, la int 3instrucción se coloca antes del código de asignación, como puede ver arriba. En x86, esa instrucción se coloca después del código de asignación. Eso explica por qué VS se está rompiendo temprano solo en x64. Es difícil decir si esto es culpa de Visual Studio o del compilador JIT. No estoy seguro de qué aplicación inserta ganchos de puntos de interrupción.