Câu trả lời của Douglas là đúng về mã chết tối ưu hóa JIT ( cả trình biên dịch x86 và x64 sẽ làm điều này). Tuy nhiên, nếu trình biên dịch JIT đang tối ưu hóa mã chết thì điều đó sẽ hiển nhiên ngay lập tức vì xthậm chí sẽ không xuất hiện trong cửa sổ Người dân địa phương. Hơn nữa, thay vào đó, cửa sổ xem và ngay lập tức sẽ cung cấp cho bạn một lỗi khi cố gắng truy cập nó: "Tên 'x' không tồn tại trong ngữ cảnh hiện tại". Đó không phải là những gì bạn đã mô tả là đang xảy ra.
Những gì bạn đang thấy thực sự là một lỗi trong Visual Studio 2010.
Đầu tiên, tôi đã cố gắng tạo lại sự cố này trên máy chính của mình: Win7x64 và VS2012. Đối với các mục tiêu .NET 4.0, xbằng 3.0D khi nó ngắt trên dấu ngoặc nhọn đóng. Tôi cũng quyết định thử các mục tiêu .NET 3.5 và cùng với đó, xcũng được đặt thành 3.0D, không phải null.
Vì tôi không thể tái tạo hoàn hảo vấn đề này vì tôi đã cài đặt .NET 4.5 trên .NET 4.0, tôi đã tạo một máy ảo và cài đặt VS2010 trên đó.
Tại đây, tôi đã có thể tái tạo vấn đề. Với một điểm ngắt trên dấu ngoặc nhọn đóng của Mainphương thức, trong cả cửa sổ theo dõi và cửa sổ định vị, tôi thấy điều đó xlà đúng null. Đây là nơi mà nó bắt đầu trở nên thú vị. Thay vào đó, tôi đã nhắm mục tiêu thời gian chạy v2.0 và thấy rằng nó cũng không có giá trị ở đó. Chắc chắn đó không thể là trường hợp vì tôi có cùng một phiên bản thời gian chạy .NET 2.0 trên máy tính khác của tôi đã hiển thị thành công xvới giá trị là 3.0D.
Vậy, chuyện gì đang xảy ra vậy? Sau một số tìm hiểu trong windbg, tôi đã tìm thấy vấn đề:
VS2010 đang hiển thị cho bạn giá trị của x trước khi nó thực sự được gán .
Tôi biết đó không phải là những gì nó trông như thế nào, vì con trỏ hướng dẫn ở quá x = y + zdòng. Bạn có thể tự kiểm tra điều này bằng cách thêm một vài dòng mã vào phương pháp:
double? y = 1D;
double? z = 2D;
double? x;
x = y + z;
Console.WriteLine();
Với một điểm ngắt trên dấu ngoặc nhọn cuối cùng, các địa phương và cửa sổ đồng hồ hiển thị xbằng 3.0D. Tuy nhiên, nếu bạn bước qua mã, bạn sẽ nhận thấy rằng VS2010 không hiển thị xlà được chỉ định cho đến khi bạn bước qua Console.WriteLine().
Tôi không biết liệu lỗi này đã từng được báo cáo với Microsoft Connect chưa, nhưng bạn có thể muốn làm điều đó, với mã này làm ví dụ. Tuy nhiên, rõ ràng nó đã được sửa trong VS2012, vì vậy tôi không chắc liệu sẽ có bản cập nhật để sửa lỗi này hay không.
Đây là những gì đang thực sự xảy ra trong JIT và VS2010
Với mã gốc, chúng ta có thể thấy VS đang làm gì và tại sao nó lại sai. Chúng tôi cũng có thể thấy rằng xbiến không được tối ưu hóa (trừ khi bạn đã đánh dấu assembly là được biên dịch với tối ưu hóa được bật).
Trước tiên, hãy xem xét các định nghĩa biến cục bộ của 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)
Đây là đầu ra bình thường ở chế độ gỡ lỗi. Visual Studio xác định các biến cục bộ trùng lặp sử dụng trong quá trình gán và sau đó thêm các lệnh IL bổ sung để sao chép nó từ biến CS * sang biến cục bộ tương ứng do người dùng xác định. Đây là mã IL tương ứng cho thấy điều này đang xảy ra:
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
Hãy thực hiện một số gỡ lỗi sâu hơn với WinDbg:
Nếu bạn gỡ lỗi ứng dụng trong VS2010 và để lại một điểm ngắt ở cuối phương pháp, chúng ta có thể đính kèm WinDbg một cách dễ dàng, ở chế độ không xâm lấn.
Đây là khung cho Mainphương thức trong ngăn xếp cuộc gọi. Chúng tôi quan tâm đến IP (con trỏ hướng dẫn).
0: 009>! Clrstack
Id chuỗi hệ điều hành: 0x135c (9)
Trang web cuộc gọi IP SP con
000000001c48dc00 000007ff0017338d ConsoleApplication1.Program.Main (System.String [])
[Và như thế...]
Nếu chúng tôi xem mã máy gốc cho Mainphương thức, chúng tôi có thể xem những hướng dẫn nào đã được chạy tại thời điểm VS phá vỡ thực thi:
000007ff`00173388 e813fe25f2 gọi 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 rcx bước, [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 movap xmm6, xmmword ptr [rsp + 0C0h]
000007ff`001733cf 4881c4d8000000 thêm rsp, 0D8h
000007ff`001733d6 c3 ret
Sử dụng IP hiện tại mà chúng ta nhận được từ !clrstacktrong Main, chúng ta thấy thực hiện mà đã bị đình chỉ trên hướng dẫn trực tiếp sau khi cuộc gọi đến System.Nullable<double>của constructor. ( int 3là ngắt được sử dụng bởi trình gỡ lỗi để ngừng thực thi) Tôi đã bao quanh dòng đó bằng * 's và bạn cũng có thể so khớp dòng với L_0056trong IL.
Hợp ngữ x64 theo sau thực sự gán nó cho biến cục bộ x. Con trỏ hướng dẫn của chúng tôi chưa thực thi mã đó, vì vậy VS2010 sớm bị hỏng trước khi xbiến được gán bởi mã gốc.
CHỈNH SỬA: Trong x64, int 3lệnh được đặt trước mã gán, như bạn có thể thấy ở trên. Trong x86, lệnh đó được đặt sau mã gán. Điều đó giải thích tại sao VS chỉ phá vỡ sớm trong x64. Thật khó để nói đây là lỗi của Visual Studio hay trình biên dịch JIT. Tôi không chắc ứng dụng nào chèn móc ngắt điểm.