代码在发布与调试模式下的行为不同

在Release模式和debug模式下运行时,我们有一些unit testing失败。 如果我在发布模式下附加调试器,则测试通过。 有太多的代码要在这里发布,所以我真的只是在寻找调试发布模式问题的最佳实践。 我检查过:

  • DEBUG和RELEASE预处理器指令,但我没有找到任何指令。
  • 条件方法

解决方案:在这种情况下,这是因为我在比较浮点变量是否相等。 在没有重大重构的情况下,我无法将浮点数更改为十进制数,因此我添加了一个扩展方法:

public static class FloatExtension { public static bool AlmostEquals(this float f1, float f2, float precision) { return (Math.Abs(f1 - f2) <= precision); } public static bool AlmostEquals(this float f1, float f2) { return AlmostEquals(f1, f2, .00001f); } public static bool AlmostEquals(this float? f1, float? f2) { if (f1.HasValue && f2.HasValue) { return AlmostEquals(f1.Value, f2.Value); } else if (f1 == null && f2 == null) { return true; } return false; } } 

由于它似乎是浮点相关的,所以有很多事情都可能出错。 请参阅: C# – 在.NET 上32位和64位以及双精度问题上的 数学运算结果不一致

有很多东西可以用浮点来破坏。 比较花车的平等是一般的禁忌。 你可以检查小于合理epsilon的差异。

可能导致您看到的行为的一件事是导致竞争条件的错误。 附加调试器可以更改代码的时序,从而不再触发竞争条件。

要修复它,只要有多个线程访问数据,就要适当地使用同步。


我正在比较IsEqual方法中的一些浮点值。

这听起来是一个非常糟糕的主意。 您不应该将浮点数比较为相等,因为浮点计算不是100%精确,您可以获得表示和舍入错误。 比较看它们是否足够接近。 对于涉及金钱的计算,您可能希望使用decimal类型。

你应该问自己的问题 –

  1. 我的代码是否有线程? 时间差异会影响输出
  2. 有人用一个副作用的表达式调用Debug.Assert()吗?
  3. 哪些对象实现了IDisposable()并以某种方式执行更改状态?
  4. 你P /调用非托管代码?

在这种情况下,3号很可能是坏孩子。 在调试和发布中垃圾收集可能会有很大差异,您可能会发现当对象被垃圾收集时会影响后续unit testing的结果。

还有,如果您正在使用NUnit和TestDriven.NET,那么两个运行测试的顺序不同

这通常就是这种情况,因为默认情况下调试版本没有优化,即使你启用它,调试时的行为也会大不相同。 您可以在Properties-> Build选项卡上为所有程序集的项目设置禁用“Optimize code”。

当然还有其他可能导致差异的变化,比如你提到条件方法就是其中之一。 这些我发现很少是问题的原因,对我而言,它几乎总是优化者。

优化器的经典问题包括“内联”的方法,以便它们不会出现在调用堆栈中。 使用System.Diagnostics.StackFrame类来确定当前执行点时,这会导致问题。 类似地,这将影响MethodBase.GetCurrentMethod的结果或依赖于执行方法的其他函数/行为。

然后有很多我见过优化器的东西,根本无法解释。 一个这样的例子被记录并在一篇文章“ HashDerivedBytes ”中进行了讨论- 取代了Rfc2898DeriveBytes,但为什么呢? “但我从未解开过这个谜团。 我只知道优化器只是在用于生成一系列派生字节时破坏了Rfc2898DeriveBytes。 奇怪的是,只有当生成的字节不能被所使用的散列算法的大小整除时(20)并且仅在前20个字节之后产生不正确的结果时,这才会破坏。

事实上,对代码产生负面影响的优化并不是编译器的新事物。 大多数老式的C ++开发人员会立即告诉你,然后就像我一样,进入一些关于他们如何解决它的长篇故事;)

正如马克所说,这通常是与时间相关的问题的结果,通常是竞争条件或同步问题。

处理此类问题的一种常见方法是在受影响的区域中使用“打印”语句来向您显示正在发生的事情。 如果print语句( Console.WriteLineResponse.Write ,logging或其他)使问题消失,请将值存储在全局变量中,并在问题出现后打印全局变量。

最近发生在我身上的时间是从串口读取的代码。 调试活动引起的时序变化足以影响串口的字节缓冲方式,从而改变了缓冲区的解析方式。 由于print语句改变了时序,我不得不将数据存储到输出中。

只是为了加上我的两分钱,我最近发现我在测试调用的sql过程中进行了日期比较。 这些日期都是在测试过程之前自动生成的,并且值被插入到DB中,因此它们偶尔会完全相同(使用RunTests时),导致在表连接上返回null。 不是我所期待的。 显然,在调试模式下,由于我正在慢慢地进行调试,因此自动生成时间会有所不同,这意味着我从未遇到错误。 我通过插入解决了这个问题

Threading.Thread.Sleep(520)

只要行动之间肯定存在延迟。 问题已解决。