.NET 4代码合同:我需要两次包含相同的合同吗?

时间:2010-06-29 13:24:16

标签: c# .net-4.0 code-contracts

假设我有一个方法public void Foo(string bar),调用者不应该调用空值bar。假设我还有一个方法,称之为private void FooImpl(string bar),它可以完成Foo的实际工作。当然,FooImpl 确实要求bar的非空,即使Foo是公共接口。并且假设我想使用.NET 4.0代码契约强制执行此非null。

我在哪里签合同?

如果我这样做:

public void Foo(string bar)
{
  this.FooImpl(bar);
}

private void FooImpl(string bar);
{
  Contract.Requires<ArgumentNullException>(bar != null);

  // Something that requires non-nullness, e.g.:
  bar.Contains("test");
}

然后静态检查器抱怨Foo正在使用可能为空的值调用FooImpl,并建议我将非空合约添加到Foo。好的,所以我想我不能将我的合同检查/异常抛出委托给实现方法。

但如果我试图把它放在公共界面,即:

public void Foo(string bar)
{
  Contract.Requires<ArgumentNullException>(bar != null);

  this.FooImpl(bar);
}

private void FooImpl(string bar);
{
  bar.Contains("test");
}

然后静态检查器抱怨FooImpl在可能为空的值上调用Contains ---即使代码中FooImpl被调用的唯一地方来自Foo,它本身确保永远不会使用空值调用FooImpl


那么,我需要两次包含相同的合同吗?或者我应该忽略静态检查器?我知道这是繁忙工作的一种来源,不应该依赖,但我希望它有一些方法来处理这个基本的,可能是常见的情景。

2 个答案:

答案 0 :(得分:3)

简短回答:是的。

您应该在希望代码约定的任何地方添加前置条件,以防止出现无引用异常等问题。这有时意味着看起来像你要两次添加相同的合同。

在这种特殊情况下,很明显,FooImpl仅从已经具有前置条件的方法调用。

但是,静态检查器独立于其他方法评估FooImpl方法。 在这个例子中,你只需从Foo传递条形值(你知道bar不是null),但是静态检查器不能确定你没有操纵bar,可能导致它为null。

此外,您应该考虑将来可能从没有前提条件的方法调用FooImpl方法来检查bar是否为null。您希望静态检查程序可以防止在这些情况下发生空引用异常。

答案 1 :(得分:1)

为了安抚静态检查器,我可能在内部函数上使用Contract.Assume(),而不是正常的契约函数,所以它不是现有契约的精确副本 - 你不想要或者在内部函数中需要实际的运行时检查。

至于为什么它不够聪明地推断出这个功能只能从另一个地方调用,我不确定。