代码合同/声明:重复检查怎么办?


10

我非常喜欢用我使用的语言编写断言,合同或任何类型的支票。让我感到困扰的一件事是,我不确定处理重复支票的通用做法。

情况示例:我首先编写以下函数

void DoSomething( object obj )
{
  Contract.Requires<ArgumentNullException>( obj != null );
  //code using obj
}

然后几个小时后,我编写了另一个函数,该函数调用第一个函数。由于所有内容仍在内存中,因此我决定不重复合同,因为我知道DoSomething已经检查了一个空对象:

void DoSomethingElse( object obj )
{
  //no Requires here: DoSomething will do that already
  DoSomething( obj );
  //code using obj
}

明显的问题:DoSomethingElse现在依赖于DoSomething验证obj不为null。因此,应该DoSomething决定不再检查,或者如果我决定使用另一个函数,则可能不再检查obj。毕竟,这导致我要编写此实现:

void DoSomethingElse( object obj )
{
  Contract.Requires<ArgumentNullException>( obj != null );
  DoSomething( obj );
  //code using obj
}

始终安全,无后顾之忧,只不过如果情况恶化,则可能会多次检查同一对象,并且这是一种重复形式,我们都知道这样做不是很好。

对于这种情况,最常见的做法是什么?


3
ArgumentBullException?这是一个新的:)
CVn

大声笑@我的打字技巧...我会对其进行编辑。
stijn 2011年

Answers:


13

就个人而言,我会在任何会返回null的函数中检查null,而不在不会的任何函数中进行检查。

因此,在上面的示例中,如果doSomethingElse()不需要取消引用obj,那么我不会在那里检查obj是否为null。

如果DoSomething()确实取消了对obj的引用,则应检查是否为null。

如果两个函数都取消引用,则它们都应检查。因此,如果DoSomethingElse取消引用obj,则它应检查是否为null,但DoSomething仍应检查是否为null,因为它可能是从其他路径调用的。

这样,您可以使代码保持清晰,并仍然确保检查在正确的位置。


1
我完全同意。每种方法的前提条件应独立存在。想象一下,您重写后DoSomething()不再需要先决条件(在这种情况下不太可能发生,但可能在其他情况下发生),然后删除先决条件检查。现在,由于缺少先决条件,一些看似完全不相关的方法被打破了。我将对一点重复的代码进行澄清,以澄清奇怪的故障,例如出于每天都希望保存几行代码的渴望。
的CVn

2

大!我看到您发现了有关.NET的代码合同。代码合同比您的平均断言超出了很多,静态检查器是最好的例子。如果未安装Visual Studio Premium或更高版本,则可能无法使用该功能,但是如果要使用代码协定,则必须了解其背后的意图,这一点很重要。

当您将合同应用于功能时,它实际上就是合同。该函数保证根据合同行为,并保证仅按合同定义使用。

在您给出的示例中,该DoSomethingElse()函数不符合所指定的约定DoSomething(),因为可以传递null,并且静态检查器将指出此问题。解决此问题的方法是在中添加相同的合同DoSomethingElse()

现在,这意味着将有重复项,但是当您选择将功能公开给两个功能时,此重复项是必需的。这些函数虽然是私有的,但也可以从类中的不同位置调用这些函数,因此,确保从任何给定调用中引出的参数永远不会为null的唯一方法是复制合同。

这应该使您重新考虑为什么首先将行为分为两个功能。我一直认为(与普遍看法相反),您不应拆分仅从一个地方调用的函数。通过应用合同公开封装,这变得更加明显。看来我为自己的事业找到了额外的理由!谢谢!:)


关于您的最后一段:在实际代码中,两个函数都是两个不同类的成员,这就是为什么将它们拆分的原因。除此之外,我处于以下情况多次:写一个冗长的函数,决定不拆分它。后来发现某些逻辑在其他地方重复,因此无论如何都要拆分它。或一年后,再次阅读并发现它不可读,因此无论如何都要对其进行拆分。或在调试时:拆分功能=无需再按F10键。还有更多的原因,所以我个人更喜欢拆分,即使这有时可能太极端了。
stijn 2011年

(1)“后来弄清楚某些逻辑在其他地方重复”。这就是为什么我发现始终“朝着API开发”比简单地拆分功能更重要的原因。不断考虑重用,而不仅仅是在当前类中。(2)“或者一年后再读一次,发现它不可读”因为函数具有名称,这样更好吗?如果您在我的博客上使用被称为“代码段” 的评论者,则将具有更高的可读性。(3)“拆分功能=减少F10键的敲击声” ...我不知道为什么。
史蒂文·杰里斯

(1)同意(2)可读性是个人喜好,因此对于我来说,这并不是真正要讨论的内容。(3)进行20行功能需要击中F10 20次。通过一个在拆分函数中具有10条这些行的函数,需要我选择只击F10 11次。是的,在第一种情况下,我可以放置断点或选择“跳转到光标”,但是与第二种情况相比,这仍然需要更多的精力。
stijn 2011年

@stijn:(2)同意; p(3)感谢您的澄清!
史蒂文·杰里斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.