2010-06-29 32 views
2

比方说,我有一个方法public void Foo(string bar)调用方不应使用bar的空值调用。比方说,我也有一个方法,叫做private void FooImpl(string bar),它的实际工作是Foo。当然,FooImpl确实要求非空值bar,即使Foo是公共接口。并且可以说我想要使用.NET 4.0代码合同强制实现这种非nullness。.NET 4代码合同:我需要包含两次相同的合同吗?

我该在哪里签合同?

如果我这样做:

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


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

回答

3

简答:是的。

你应该在任何你想要的代码合同中添加前提条件来防止诸如nullreference异常之类的事情。这有时候意味着它看起来像,您将两次添加相同的合约。

在这种特殊情况下,很明显FooImpl只能从已经具有先决条件的方法中调用。

但是,静态检查器独立于其他方法评估FooImpl方法。 在这个例子中,你只是传递来自Foo的bar值(你知道bar不为null),但是静态检查器不能确定你操作的是否是,可能导致它为空。

另外,您应该考虑将来您可能会从不具有检查bar是否为空的前提条件的方法调用FooImpl方法。你也希望静态检查器防止nullreference异常在这些情况下发生。

1

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

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

+2

假设添加运行时检查(取决于您选择的级别)。如果您使用“仅限表面合同”,则无论如何,所有合同都将从内部函数中剥离。这并不是Assume设计的目的;使用需求。 – porges 2010-07-04 22:36:31