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

时间:2011-01-24 20:57:22

标签: c# .net release

在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;
    }
}

6 个答案:

答案 0 :(得分:8)

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

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


  

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

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

答案 1 :(得分:3)

由于它似乎是浮点相关的,所以有很多东西可能出错。看到: C# - Inconsistent math operation result on 32-bit and 64-bitDouble precision problems on .NET

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

答案 2 :(得分:2)

你应该问自己的问题 -

  1. 我的代码是否有线程?时序差异将影响输出
  2. 有人用一个带副作用的表达式调用Debug.Assert()吗?
  3. 哪些对象实现了IDisposable()并以改变状态的方式执行了哪些操作?
  4. 您是否正在调用非托管代码?
  5. 在这种情况下,3号很可能是坏孩子。在调试和发布时,垃圾收集可能会有很大的不同,您可能会发现当对象被垃圾收集时会影响后续单元测试的结果。

    如果您使用的是NUnit和TestDriven.NET,我们将在不同的订单中进行两次测试。

答案 3 :(得分:2)

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

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

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

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

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

答案 4 :(得分:0)

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

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

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

答案 5 :(得分:0)

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

解决了这个问题

Threading.Thread.Sleep(520)

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