测试前提条件的不同方法的优缺点?

时间:2010-12-15 01:19:20

标签: c# constraints assert code-contracts preconditions

在我的脑海中,我可以想到4种方法来检查空参数:

Debug.Assert(context != null);
Contract.Assert(context != null);
Contract.Requires(context != null);
if (context == null) throw new ArgumentNullException("context");

我一直使用最后一种方法,但我刚看到一个使用Contract.Requires的代码片段,我对此并不熟悉。 每种方法有哪些优点/缺点?还有其他方法吗?


在VS2010 w / Resharper中,

  • Contract.Assert警告我表达式总是正确的(它怎么知道,我不太确定...... HttpContext不能为空?),
  • Contract.Requires逐渐消失,它告诉我编译器不会调用该方法(我假设因为前一个原因,它永远不会为null),并且
  • 如果我将最后一个方法更改为context != null,则后面的所有代码都会逐渐消失,它会告诉我代码是启发式无法访问的。

所以,似乎最后3种方法在VS静态检查器中内置了某种智能,而Debug.Assert只是愚蠢。

2 个答案:

答案 0 :(得分:12)

我的猜测是,有一个契约应用于接口IHttpHandler.ProcessRequest,需要上下文!= null。接口契约由其实现者继承,因此您无需重复Requires。实际上,您不能添加其他Requires语句,因为您仅限于与接口契约相关的要求。

我认为在指定合同义务与简单执行空检查之间进行区分非常重要。您可以在运行时实现空检查并抛出异常,以通知开发人员他们正在使用您的API。另一方面,契约表达式实际上是一种元数据形式,可以由合同重写器解释(引入先前手动实现的运行时异常),也可以由静态分析器解释,静态分析器可以使用它们来推理关于你的申请的静态正确性。

也就是说,如果您在一个正在积极使用代码约定和静态分析的环境中工作,那么将这些断言置于契约形式中是绝对可取的,以利用静态分析。即使您没有使用静态分析,您仍然可以通过使用合同为以后的利益敞开大门。需要注意的是,您是否已将项目配置为执行重写,否则合同将不会像您预期的那样导致运行时异常。


要详细说明评论者所说的内容,Assert,Assume和Requires之间的区别是:

  • Contract.Assert表达式由合同重写器转换为断言,静态分析器尝试根据其现有证据证明该表达式。如果无法证明,您将收到静态分析警告。
  • Contract.Assume表达式被合同重写者忽略(据我所知),但静态分析器将其解释为静态分析中可以考虑的新证据。 Contract.Assume用于“填补静态分析中的空白”,无论是缺乏进行必要推理的复杂程度还是与未经合同修饰的代码互操作,以便您可以假设,例如,特定的函数调用返回非null结果。
  • Contract.Requires是调用方法时必须始终为true的条件。它们可以是对方法参数的约束(这是最典型的),它们也可能是对象的公开可见状态的约束(例如,如果Initialized为True,您可能只允许调用该方法。)这些类型约束推动你的类的用户在使用对象时检查Initialized(如果不是,可能会适当地处理错误)或者创建他们自己的约束和/或类不变量来澄清初始化确实发生了。

答案 1 :(得分:2)

第一种方法适用于测试永远不存在的空条件。也就是说,在开发期间使用它以确保它不会意外地设置为null。由于它不执行任何错误处理,因此不适合处理已发布产品中的空条件。

我想说第二版和第三版的相似之处在于他们不会以任何方式处理这个问题。

通常,如果变量在最终产品中实际上可能为空,则最后一个版本是要使用的版本。你可以在那里做特殊处理,或者只是像你一样提出异常。