2

假设我有一个public void Foo(string bar)调用者不应该使用 null 值调用的方法bar。假设我也有一个方法,调用它private void FooImpl(string bar),它可以完成Foo. 当然,FooImpl确实需要 的非空性bar,即使Foo是公共接口。假设我想使用 .NET 4.0 代码合同来强制执行这种非空性。

我把合同放在哪里?

如果我这样做:

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在代码中调用的唯一位置是 from Foo,它本身确保它永远不会FooImpl使用空值调用。


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

4

2 回答 2

3

简短的回答:是的。

您应该在希望代码合同的任何地方添加先决条件,以防止出现空引用异常之类的事情。这有时意味着您看起来像是两次添加相同的合同。

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

但是,静态检查器独立于其他方法评估 FooImpl 方法。在此示例中,您只是从 Foo 传递 bar 值(您知道 bar 不是 null ),但静态检查器不确定您没有操作 bar,可能导致它为 null。

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

于 2010-06-29T13:38:13.067 回答
1

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

至于为什么它不够聪明,无法推断出这个函数只是从另一个地方调用的,我不确定。

于 2010-06-29T13:32:24.167 回答