【发布时间】:2014-09-08 17:55:05
【问题描述】:
我们的一个项目的一个类中有以下方法:
private unsafe void SomeMethod()
{
// Beginning of the method omitted for brevity
var nv = new Vector4[x];
fixed (Vector4* vp = nv)
{
fixed (float* tp = /* Source float ptr */)
{
fixed (double* ap = /* Source double ptr */)
{
for (var i = atlArray.Length - 1; i >= 0; --i)
{
vp[((i + 1) << 3) - 2] = new Vector4(tp[i], btt, 0.0f, 1.0f);
// Additional Vector4 construction omitted for brevity
nttp[i] = new Vector2(tp[i], this.ttvp);
nts[i] = string.Format(ap[i], /* etc. */);
}
}
}
}
this.ts = nts;
this.ttp = nttp;
this.V = nv; // <- This is a property setter
}
我不得不对此进行混淆,但希望它仍然足够清晰,可以了解发生了什么。
在我们开发人员的一台机器上,在调试版本中,C# 编译器会删除在 fixed 块关闭后发生的三个赋值。如果我们尝试在这些行上放置断点,则断点会在应用程序启动时跳到方法的结束大括号。出现在fixed 块和方法结尾之间的代码也会在其他方法中被删除,但令人费解的是,并非所有方法都删除。
经过一些实验,我们发现为受影响的项目启用优化会导致包含缺失的代码。但是,这种变通方法对我们的单元测试失败了——缺少代码,更改受影响项目及其测试项目的优化也无济于事。我们还发现,在最里面的 fixed 语句中移动三个赋值是有效的——在检查 IL 时就很清楚为什么了。
在受影响机器上构建的调试DLL中(关闭优化),在ap出栈后直接出现return op:
IL_03a1: nop
IL_03a2: ldc.i4.0
IL_03a3: conv.u
IL_03a4: stloc.s ap
IL_03a6: ret
这解释了为什么在 stloc 指令之前移动三个分配有效。在我的机器上构建的调试DLL中,返回操作发生在预期的位置,在三个赋值之后:
IL_03a5: nop
IL_03a6: ldc.i4.0
IL_03a7: conv.u
IL_03a8: stloc.s ap
IL_03aa: nop
IL_03ab: ldc.i4.0
IL_03ac: conv.u
IL_03ad: stloc.s tp
IL_03af: nop
IL_03b0: ldc.i4.0
IL_03b1: conv.u
IL_03b2: stloc.s vp
IL_03b4: ldarg.0
IL_03b5: ldloc.s nts
IL_03b7: stfld string[] N.B.E.B::ts
IL_03bc: ldarg.0
IL_03bd: ldloc.s nttp
IL_03bf: stfld valuetype [SharpDX]SharpDX.Vector2[] N.B.E.B::ttp
IL_03c4: ldarg.0
IL_03c5: ldloc.s nv
IL_03c7: call instance void N.B.E.B::set_V(valuetype [SharpDX]SharpDX.Vector4[])
IL_03cc: nop
IL_03cd: ret
到目前为止,我们未能产生 SSCCE - 这似乎只在非常特定的情况下表现出来,并且只在我们的一个项目中表现出来。我们检查了两台机器上都使用了相同版本的 Visual Studio、.NET 框架、C# 编译器和 MSBuild。我们检查了其他潜在差异,例如操作系统版本和更新。两台机器上的情况似乎相同(它们是相同型号的笔记本电脑)。坦率地说,我们有点困惑。任何帮助将不胜感激。
【问题讨论】:
标签: c# visual-studio-2013 il