2

我的代码库有很多#if DEBUG/#endif语句,它们大多具有断言类型逻辑,我没有足够的勇气在生产中运行。

[Conditional("DEBUG")]
public void CheckFontSizeMath()
{
  //This check must not block SellSnakeOil() in production, even if it fails.
  if(perferredSize+increment!=increment+preferredSize)
    throw new WeAreInAnUnexpectedParallelUniverseException();
}

我会后悔把所有这些都换成新的做事方式吗?

更新:我正在寻找两种相似但不同的语法风格的特征之间的差异来进行断言。我知道还有很多其他方法可以演示应用程序的工作,我也这样做了。我还没准备好完全放弃断言。

我还更新了实际调试发布场景的方法名称。

4

4 回答 4

3

这样做没有问题。更当前的方法是使用Code Contracts

于 2010-08-26T18:38:01.267 回答
2

写测试!然后,您不必担心您的代码正在执行您开始进行这些更改时未预料到的事情。

一旦你的代码被测试,你就可以重构到你心中的内容。

没有测试的代码,即使是昨天写的,也是遗留代码……获取一份有效地使用遗留代码工作的副本,这是一本管理遗留代码的好书;32 条评论,5 星。

于 2010-08-26T18:39:08.117 回答
1

您拥有的其他选项是使用 Hans 建议的代码合同或使用System.Diagnostics.Debug命名空间中的断言方法,例如

Debug.Assert(1 + 1 == 2, "Math went wrong!");

构建发布版本时会自动删除此检查。

于 2010-08-26T18:47:26.557 回答
1

缺点:

  1. 它不如独立于构建配置运行的硬检查和异常强大。考虑到大多数代码应该在发布模式下处理错误是一个好主意。

  2. 行为有可能在配置之间有所不同。如果你粗心,你最终可能会在条件方法中放置一些非调试逻辑或添加副作用,从而使你的生产代码无法正常运行(基本上与在 C++ 中的 ASSERT() 宏中意外调用方法相同;侧效果糟透了!)这里最重要的是例外。条件方法不允许您出于任何原因返回值,以消除一个潜在错误。但是,它们确实允许您抛出异常,这些异常可以显着改变发生错误情况时所采用的路径。这使得事情更难理解和维护。

  3. 在调用站点,条件方法被调用并不明显。你只会看到“DoSomething();”。我更喜欢通过我知道它是条件的约定来命名我的条件方法;例如 DEBUG_SanityCheckBufferContents()。

  4. 如果您没有#if 方法体,您的调试代码将仍然存在,并且可以通过反射等方式检查/调用它。该方法的 IL 仍然发出,调用站点没有。

  5. 它使单元测试非常困难,因为配置之间的行为不同。这与第 2 点的思路相同,但我将它作为一个单独的点添加,因为从测试的角度来看它真的很糟糕。大多数开发人员会在签入之前在调试或发布中运行测试,但不会同时运行两者。我们的 CI 运行两组测试,因此在通过所有测试后检查某些内容却发现您已经破坏了调试配置,但测试通过了,因为您运行了发布版本(反之亦然)。

简而言之,我的经验法则是:

  • 更喜欢单元测试的硬检查和异常。在不可能的情况下使用条件方法(例如性能关键检查)
  • 清楚地命名条件方法
  • Pragma #if out 方法体中的代码
  • 非常警惕在条件方法中改变程序控制流(在使用条件方法时,我个人更喜欢 Debug.Assert 而不是异常,但我已经看到很多其他程序员使用异常,所以这可能是有争议的)
于 2010-08-26T18:50:47.810 回答