C# 双人间?=双份的+;双重的

C# 双人间?=双份的+;双重的,c#,.net,C#,.net,我想对StackOverflow社区进行ping,看看我是否对这个简单的C#代码失去了理智 我在Windows7上开发,在.NET4.0、x64调试中构建它 我有以下代码: static void Main() { double? y = 1D; double? z = 2D; double? x; x = y + z; } 如果我调试并在结尾的大括号上放置一个断点,我希望监视窗口和即时窗口中的x=3。x=null 如果我在x86中调试,事情似乎运行良好。x6

我想对StackOverflow社区进行ping,看看我是否对这个简单的C#代码失去了理智

我在Windows7上开发,在.NET4.0、x64调试中构建它

我有以下代码:

static void Main()
{
    double? y = 1D;
    double? z = 2D;

    double? x;
    x = y + z;
}
如果我调试并在结尾的大括号上放置一个断点,我希望监视窗口和即时窗口中的x=3。x=null


如果我在x86中调试,事情似乎运行良好。x64编译器有问题还是我有问题?

众所周知,x64 JIT编译器在优化方面比x86编译器更具攻击性。(对于x86和x64编译器生成语义不同的代码的情况,可以参考“”

在这种情况下,x64编译器检测到
x
从未被读取,并完全取消其赋值;这就是所谓的编译优化。为了防止这种情况发生,只需在分配之后添加以下行:

Console.WriteLine(x);
您将观察到,不仅会打印
3
的正确值,而且变量
x
也会在调试器中显示正确的值(编辑),并跟随
控制台。WriteLine
调用引用它


编辑:Christopher Currens指出了Visual Studio 2010中的一个错误,这可能比上述更准确。

道格拉斯关于JIT优化死代码的回答是正确的(x86和x64编译器都会这样做)。然而,如果JIT编译器正在优化死代码,这将是显而易见的,因为
x
甚至不会出现在本地窗口中。此外,watch and immediate窗口在尝试访问它时会给您一个错误:“名称“x”在当前上下文中不存在”。这不是你所说的正在发生的事情

您看到的实际上是VisualStudio2010中的一个bug

首先,我试图在我的主机上重现这个问题:Win7x64和VS2012。对于.NET 4.0目标,
x
在右大括号上断裂时等于3.0D。我决定也尝试一下.NET3.5目标,这样,
x
也被设置为3.0D,而不是null

由于在.NET4.0的基础上安装了.NET4.5,所以我无法完美地再现这个问题,因此我启动了一个虚拟机并在其上安装了VS2010

在这里,我能够重现这个问题。在watch窗口和locals窗口中,在
Main
方法的右括号中有一个断点,我看到
x
null
。这就是它开始变得有趣的地方。我将目标对准了v2.0运行时,发现它在那里也是空的。当然不会是这样,因为我的另一台计算机上有相同版本的.NET 2.0运行时,它成功地显示了
x
,值为
3.0D

那么,发生了什么事?在windbg中进行了一些挖掘之后,我发现了问题:

VS2010在实际分配x之前向您显示x的值

我知道这不是它看起来的样子,因为指令指针超过了
x=y+z
行。您可以通过向方法中添加几行代码来测试这一点:

double? y = 1D;
double? z = 2D;

double? x;
x = y + z;

Console.WriteLine(); // Don't reference x here, still leave it as dead code
在最后一个大括号上有一个断点时,“局部变量和监视”窗口显示的
x
等于
3.0D
。但是,如果您一步一步地浏览代码,您会注意到VS2010直到您通过
控制台.WriteLine()
之后才显示为已分配的
x

我不知道是否有人向Microsoft Connect报告过此错误,但您可能希望以此代码为例进行报告。它显然已经在VS2012中修复,所以我不确定是否会有更新来修复这个问题


以下是JIT和VS2010中实际发生的情况

使用原始代码,我们可以看到VS正在做什么以及为什么它是错误的。我们还可以看到,
x
变量没有得到优化(除非您将程序集标记为在启用优化的情况下编译)

首先,让我们看看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)
如果我们查看
Main
方法的本机代码,我们可以看到VS中断执行时运行了哪些指令:

000007ff`00173388 e813fe25f2 call 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 add rsp,0D8h 000007ff`001733d6 c3 ret 000007ff`00173388 e813fe25f2呼叫mscorlib_ni+0xd431a0 (000007fe`f23d31a0)(System.Nullable`1[[System.Double,mscorlib]]…ctor(Double),mdToken:000000000 6001EF2) ****000007ff`0017338d cc int 3**** 000007ff`0017338e 8D8C249000000 lea ecx,[rsp+90小时] 000007ff`00173395 488b01 mov rax,qword ptr[rcx] 000007ff`00173398 48898424800000 mov qword ptr[rsp+80小时],rax 000007ff`001733a0 488b4108移动rax,qword ptr[rcx+8] 000007ff`001733a4 48898424880000000 mov qword ptr[rsp+88h],rax 000007ff`001733ac 488D8C248000000 lea rcx,[rsp+80小时] 000007ff`001733b4 488b01 mov rax,qword ptr[rcx] 000007ff`001733b7 4889442440 mov qword ptr[rsp+40小时],rax 000007ff`001733bc 488b4108移动rax,qword ptr[rcx+8] 000007ff`001733c0 4889442448 mov qword ptr[rsp+48小时],rax 000007ff`001733c5 eb00 jmp 000007ff`001733c7 000007ff`001733c7 0f28b424c0000000 movaps xmm6,xmmword ptr[rsp+0C0h] 000007ff`001733cf 4881c4d8000000添加rsp,0D8h 000007ff`001733d6 c3 ret 使用我们从
获得的当前IP!clrstack
Main
中,我们看到在调用
System.Nullable
的构造函数之后,指令的执行被直接挂起。(
int 3
是调试器用来停止执行的中断)我已经用*包围了这一行,您还可以将该行与IL中的
L_0056
匹配

后面的x64程序集实际上将其指定给 0:009> !clrstack OS Thread Id: 0x135c (9) Child SP IP Call Site 000000001c48dc00 000007ff0017338d ConsoleApplication1.Program.Main(System.String[]) [And so on...] 000007ff`00173388 e813fe25f2 call 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 add rsp,0D8h 000007ff`001733d6 c3 ret