Debug.Assert与Code Contract的使用

45

我应该何时使用debug.assert而不是代码合同,反之亦然?我想检查方法的前提条件,但我对选择哪种方法感到困惑。我有单元测试,想要测试失败的情况并期望异常。

在同一个方法中同时使用Debug.Assert和Code contract是否是一个好习惯。如果是,应该按什么顺序编写代码?

Debug.Assert(parameter!= null);
Contract.Requires<ArgumentNullException>(parameter != null, "parameter");
或者
Contract.Requires<ArgumentNullException>(parameter != null, "parameter");
Debug.Assert(parameter!= null);

这背后有什么道理吗?

2个回答

37

这些是不同的东西。Debug Assert仅在编译为Debug时执行,因此只在Debug下检查/断言。其想法是在开发代码时使用它进行“合理性检查”。Code contracts可在Debug和Release模式下使用。它们确保方法的前置和后置条件符合该方法的期望(满足合约)。还有一个测试框架,提供类似的功能,旨在检查测试的一致性。

在开发代码(以及后续的维护开发)时,使用Debug.Assert来确保某些事情符合您的期望。

在Debug和Release模式下,使用Code contracts来确保条件为真。合约还允许某些形式的静态分析,有助于验证程序的“正确性”。

创建单元测试时,请使用测试框架断言。


我完全同意,但如果我选择在同一位置使用debug.assert和代码合同,应该有一定的顺序吗? - Carbine
不确定为什么要以那种方式进行双重检查。顺序不应该有关系。如果我在做这个,我相信我会将Debug.Assert放在第一位作为开发调试工具。 - Dweeberly
在发布代码中不会有Debug.Assert,但是会有代码合同。因此我认为在某些情况下需要与代码合同并行使用Debug.Assert- 我可能是错的。因此提出问题。 - Carbine
3
没错,但代码合同会在调试和发布时检测到问题。如果确保在代码合同异常上断点,就能实现与Debug.Assert相同的效果。有人可能会认为除了代码合同之外再加一个Debug.Assert可以提供双重维护检查(Assert很难被忽略 :))。 - Dweeberly
9
不要忘记:“如果您想在发布版本中执行断言,请使用 Trace.Assert 方法。” (http://msdn.microsoft.com/en-us/library/tk4kachs(v=vs.110).aspx) - heltonbiker

28
个人而言,在新编写的代码中,我不会同时使用Debug.Assert和Code Contracts来强制执行先决条件 - 在我看来,Code Contracts取代了Debug.Assert,因为它们提供了更全面的检查套件,更不用说可以从在代码到运行时之前可以执行的静态检查中获得的好处。在Debug.AssertContracts中维护重复的先决条件检查将是很麻烦的。
理由:
  • 您无需重新编写任何已编码的旧版先决条件Debug.Assertthrow代码 - 您可以保留现有的先决条件检查代码并使用Contract.EndContractBlock()进行终止
  • 如果使用contract运行时检查集设置为None进行构建,则在没有/d:DEBUG的情况下构建System.Diagnostics.Debug时,可以获得相同的未经检查的“发布模式”行为参见文档中的6.2.1节
  • 合同允许开发人员在代码中更具表现力地说明检测到无效状态的“原因” - 例如,是直接由于超出带外参数 (Contract.Requires),否则Contract.AssertContract.Assume可以检查一般状态,并且使用Contract.Ensures可以表明离开方法时状态的“保证正确性”。而Invariants表示状态必须始终保持。
  • 最重要的是,静态检查可以在构建代码时强制执行这些合同 - 这样您就有机会通过设计时间或编译时间警告捕获错误,而不必等待运行时。 Contract Checks可以添加到您的持续集成中以查找不符合情况。

一个注意事项:如果您打算编写故意违反合同的单元测试,您可能需要处理ContractException - Jon Skeet在这里很好地解释了这一点。例如,在测试设置中将Contract.ContractFailed处理程序与调用SetHandled并然后抛出公共异常的处理程序绑定,您可以在UT中捕获和断言。


2
很好的解释。现在我明白了。如果一直使用,它会为开发人员关于应用程序状态的假设带来很多清晰度。这只是锦上添花,蛋糕是更详细和完整的异常处理例程,从一开始就很容易放入其中。 - jeremcc
只要有一个解决方案范围的配置设置来确保Contract.Assert不会出现在发布版本中,我就会使用它们。否则,我会使用Debug.Assert,因为我知道它们永远不会泄漏到发布版本中。在我看来,代码合同的配置维护是其最大的缺点。 - orad
在我之前的评论中,对于公共接口的前置条件和后置条件,我明白为什么我们希望在发布模式下包含它们以供消费者代码使用。但是我不明白为什么我们在任何情况下都要在发布模式下包含断言。也许我对此了解不够。 - orad
合约失败意味着代码状态的某个假设失败了 - 一些东西已经通过了验证防御。即使在生产中,我也希望知道这一点并停止执行。在大多数情况下,我愿意为此检查牺牲一些性能。 - StuartLC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接