Douglas 'Antwort bezüglich des JIT-optimierenden toten Codes ist richtig ( sowohl die x86- als auch die x64-Compiler werden dies tun). Wenn der JIT-Compiler jedoch den toten Code optimieren würde, wäre dies sofort offensichtlich, da er xnicht einmal im Fenster "Locals" angezeigt würde. Darüber hinaus würden Sie beim Versuch, darauf zuzugreifen, bei der Überwachung und dem Sofortfenster stattdessen einen Fehler erhalten: "Der Name 'x' existiert im aktuellen Kontext nicht". Das haben Sie nicht als passiert beschrieben.
Was Sie sehen, ist tatsächlich ein Fehler in Visual Studio 2010.
Zuerst habe ich versucht, dieses Problem auf meinem Hauptcomputer zu reproduzieren: Win7x64 und VS2012. xIst für .NET 4.0-Ziele gleich 3.0D, wenn die schließende geschweifte Klammer unterbrochen wird. Ich habe mich entschlossen, auch .NET 3.5-Ziele auszuprobieren, und war damit xauch auf 3.0D eingestellt, nicht auf null.
Da ich dieses Problem nicht perfekt reproduzieren kann, da ich .NET 4.5 auf .NET 4.0 installiert habe, habe ich eine virtuelle Maschine hochgefahren und VS2010 darauf installiert.
Hier konnte ich das Problem reproduzieren. Mit einem Haltepunkt in der schließenden geschweiften Klammer der MainMethode, sowohl im Überwachungsfenster als auch im Fenster der Einheimischen, sah ich, dass dies der Fall xwar null. Hier beginnt es interessant zu werden. Ich habe stattdessen auf die v2.0-Laufzeit abgezielt und festgestellt, dass sie auch dort null ist. Dies kann sicherlich nicht der Fall sein, da ich auf meinem anderen Computer dieselbe Version der .NET 2.0-Laufzeit habe, die xmit einem Wert von erfolgreich angezeigt wurde 3.0D.
Also, was passiert dann? Nach einigem Stöbern in Windbg fand ich das Problem:
VS2010 zeigt Ihnen den Wert von x an, bevor er tatsächlich zugewiesen wurde .
Ich weiß, dass es nicht so aussieht, da der Anweisungszeiger hinter der x = y + zZeile steht. Sie können dies selbst testen, indem Sie der Methode einige Codezeilen hinzufügen:
double? y = 1D;
double? z = 2D;
double? x;
x = y + z;
Console.WriteLine();
Mit einem Haltepunkt auf der letzten geschweiften Klammer werden die Einheimischen und das Beobachtungsfenster xgleich angezeigt 3.0D. Wenn Sie jedoch Schritt durch den Code, werden Sie feststellen , dass VS2010 zeigt nicht , xwie erst zugewiesen werden , nachdem Sie durch die gestufte haben Console.WriteLine().
Ich weiß nicht, ob dieser Fehler jemals an Microsoft Connect gemeldet wurde, aber Sie können dies anhand dieses Codes als Beispiel tun. Es wurde jedoch eindeutig in VS2012 behoben, daher bin ich mir nicht sicher, ob es ein Update geben wird, um dies zu beheben oder nicht.
Hier ist, was tatsächlich in der JIT und VS2010 passiert
Mit dem Originalcode können wir sehen, was VS tut und warum es falsch ist. Wir können auch sehen, dass die xVariable nicht weg optimiert wird (es sei denn, Sie haben die zu kompilierende Assembly mit aktivierten Optimierungen markiert).
Schauen wir uns zunächst die lokalen Variablendefinitionen der IL an:
.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)
Dies ist eine normale Ausgabe im Debug-Modus. Visual Studio definiert doppelte lokale Variablen, die bei Zuweisungen verwendet werden, und fügt dann zusätzliche IL-Befehle hinzu, um sie von der CS * -Variablen in die jeweilige benutzerdefinierte lokale Variable zu kopieren. Hier ist der entsprechende IL-Code, der dies zeigt:
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
Lassen Sie uns mit WinDbg ein tieferes Debugging durchführen:
Wenn Sie die Anwendung in VS2010 debuggen und am Ende der Methode einen Haltepunkt belassen, können wir WinDbg problemlos im nicht-invasiven Modus anhängen.
Hier ist der Frame für die MainMethode im Aufrufstapel. Wir kümmern uns um die IP (Anweisungszeiger).
0: 009>! Clrstack
OS-Thread-ID: 0x135c (9)
Untergeordnete SP IP-Anrufsite
000000001c48dc00 000007ff0017338d ConsoleApplication1.Program.Main (System.String [])
[Und so weiter...]
Wenn wir den nativen Maschinencode für die MainMethode anzeigen , können wir sehen, welche Anweisungen zum Zeitpunkt der Unterbrechung der Ausführung durch VS ausgeführt wurden:
000007ff`00173388 e813fe25f2 Rufen Sie mscorlib_ni + 0xd431a0 auf
(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 add rsp, 0D8h
000007ff`001733d6 c3 ret
Unter Verwendung der aktuellen IP, von der wir !clrstackin erhalten haben Main, sehen wir, dass die Ausführung der Anweisung direkt nach dem Aufruf des System.Nullable<double>Konstruktors angehalten wurde . ( int 3ist der Interrupt, der von Debuggern verwendet wird, um die Ausführung zu stoppen.) Ich habe diese Zeile mit * umgeben, und Sie können die Zeile auch L_0056in der IL abgleichen.
Die folgende x64-Assembly weist sie tatsächlich der lokalen Variablen zu x. Unser Anweisungszeiger hat diesen Code noch nicht ausgeführt, sodass VS2010 vorzeitig unterbrochen wird, bevor die xVariable vom nativen Code zugewiesen wurde.
BEARBEITEN: In x64 wird die int 3Anweisung vor dem Zuweisungscode platziert, wie Sie oben sehen können. In x86 wird diese Anweisung nach dem Zuweisungscode platziert. Das erklärt, warum VS nur in x64 früh bricht. Es ist schwer zu sagen, ob dies an Visual Studio oder dem JIT-Compiler liegt. Ich bin nicht sicher, welche Anwendung Haltepunkt-Hooks einfügt.