我们正在将应用程序迁移到.NET 4.0(从3.5开始)。我们遇到的一个问题只能在非常特殊的条件下重现:
我的意思是,如果我禁用优化和将调试信息设置为完整,则问题就会消失。
有问题的代码在.NET 3.5上工作正常,在发布模式下启用了优化等,并且已经做了很长时间。
我真的不想暗示C#编译器中存在错误,所以我的问题是我是否有任何技术可用于跟踪我们可能做错的导致错误优化的方法?
我正在尝试将此问题缩小到一个小测试用例,以便我可以在这里发布一些代码。
修改
我已将问题跟踪到以下内容:
我们在Form的构造函数中有这个代码:
public ConnectionForm()
{
LocalControlUtil.Configure("ConnectionForm", "Username", usernameLabel);
LocalControlUtil.Configure("ConnectionForm", "Password", passwordLabel);
LocalControlUtil.Configure("ConnectionForm", "Domain", domainLabel);
LocalControlUtil.Configure("ConnectionForm", "Cancel", cancelButton);
LocalControlUtil.Configure("ConnectionForm", "OK", okButton);
}
这些调用是针对某些自定义本地化代码的。从另一个程序集调用此表单的构造函数。 LocalControlUtil.Configure
方法调用Assembly.GetCallingAssembly()
,它会为所有上述调用返回正确的值,除了最后一个。
我可以对上面的行重新排序,添加新行或删除当前行,并且每次它都是最后一行不起作用。
我假设这是JIT内联调用构造函数的地方的最后一个方法调用(在另一个程序集中)。将[MethodImpl(MethodImplOptions.NoInlining)]
添加到上面的构造函数可以解决问题。
有人知道为什么会这样吗?对我来说似乎很奇怪,最后一行只能内联。这是.NET 4.0中的新行为吗?
编辑2:
我现在把它缩小到尾部调用消除,我假设由new tail-call stuff in .NET 4引起。
在上面的代码中,构造函数中对LocalControlUtil.Configure
的最后一次调用被删除并放入另一个程序集中的调用方法中。由于方法调用Assembly.GetCallingAssembly
,我们无法获得正确的程序集。
有没有办法阻止编译器(或JIT或其他任何东西)消除尾调用?
答案 0 :(得分:4)
如果不是太久,我会把它放在评论中,但你试过了吗?
public ConnectionForm()
{
try
{
LocalControlUtil.Configure("ConnectionForm", "Username", usernameLabel);
LocalControlUtil.Configure("ConnectionForm", "Password", passwordLabel);
LocalControlUtil.Configure("ConnectionForm", "Domain", domainLabel);
LocalControlUtil.Configure("ConnectionForm", "Cancel", cancelButton);
LocalControlUtil.Configure("ConnectionForm", "OK", okButton);
}
catch
{
throw;
}
}
引发任何异常的事实使得这在编写代码方面几乎是一个零更改,但try-catch边界通常会阻止编译器和抖动的优化。
答案 1 :(得分:1)
我认为你已经找到了解决问题的正确方法......将其缩小到最小的测试用例几乎总是很好的第一步。
接下来将比较为Reflector之类的每个版本生成的IL。阅读IL并非易事(除非您有丰富的经验),因此最大限度地减少您需要查看的代码量至关重要。
更新:我更多地考虑了这个问题,如果问题在IL级别无法辨别,那么它可能处于JIT(即时)水平,这对于调试。我从来没有工作过这个级别,所以我对问题的这一部分没有任何见解(如果涉及到这一点)。
答案 2 :(得分:0)
不,你不能。
.NET 4.0优化了比3.5更多的尾调用,这是一件好事。我们的代码很疯狂。