10

我正在实现我自己的 ArrayList 用于学校目的,但为了增加趣味性,我正在尝试使用 C# 4.0 代码合同。一切都很好,直到我需要将合同添加到构造函数中。我应该在空参数构造函数中添加 Contract.Ensures() 吗?

    public ArrayList(int capacity) {
        Contract.Requires(capacity > 0);
        Contract.Ensures(Size == capacity);

        _array = new T[capacity];
    }

    public ArrayList() : this(32) {
        Contract.Ensures(Size == 32);
    }

我会说是的,每种方法都应该有一个明确定义的合同。另一方面,如果它只是将工作委派给“主要”构造函数,为什么还要这样做呢?从逻辑上讲,我不需要。

我看到在两个构造函数中显式定义合约的唯一一点是,如果将来我们对合约有 Intelisense 支持。如果发生这种情况,明确每种方法具有哪些合同会很有用,就像 Intelisense 中出现的那样。

此外,是否有任何书籍可以更深入地介绍合同设计的原则和用法?一件事是了解如何在语言(在本例中为 C#)中使用合同的语法,另一件事是了解如何以及何时使用它。我阅读了几个教程和 Jon Skeet 的 C# in Depth 文章,但如果可能的话,我想更深入一点。

谢谢

4

5 回答 5

5

我完全不同意托马斯的回答。只要您在实施中做出选择ArrayList(),您就应该有一份合同来记录这些选择。

在这里,您选择使用参数 32 调用主构造函数。您可以决定做许多其他事情(不仅仅是关于默认大小的选择)。给它一个合同ArrayList()几乎与ArrayList(int)你决定不做大部分你可以做的愚蠢事情而不是直接调用它的文件的合同相同。

答案“它调用主构造函数,所以让主构造函数的合约完成工作”完全忽略了这样一个事实,即合约的存在是为了让您不必查看实现。对于基于运行时断言检查的验证策略,即使对于几乎直接调用另一个构造函数/方法的如此短的构造函数/方法,编写合约的缺点是您最终会检查两次。是的,这似乎是多余的,但运行时断言检查只是一种验证策略,而 DbC 的原理与它无关。原则是:如果它可以调用,它需要一个合同来记录它所做的事情。

于 2010-05-04T23:59:27.123 回答
1

除非您使用. ArrayList_EnsureSize == 32Ensure

所以(例如):

var x = new ArrayList();
Contract.Assert(x.Size == 32)

会给你警告“断言未证明”。

您需要明确声明所有合同;代码契约重写器/静态检查器不会“查看”方法以查看任何含义——请参阅我对相关问题的回答“我们是否必须在委派方法中冗余地指定 Contract.Requires(...) 语句?”

于 2010-05-05T22:55:47.310 回答
1

我推荐阅读Bertrand Meyer 的Object Oriented Software Construction, 2nd EditionTouch of Class。或者,您可以阅读同一作者在 1992 年发表的应用“按合同设计”的文章。

总结一下:

  • 不变量必须在构造函数(其中任何一个)完成之后,以及在执行类的任何公共方法之前和之后保持。
  • 方法前置条件后置条件是在进入和退出任何公共方法时必须保持的附加条件,以及不变量。

因此,在您的情况下,请关注不变量。无论调用哪个构造函数,都生成一个正确的对象(满足类不变量的对象)。

在这个相关的答案中,我讨论了类似的主题,包括一个例子。

于 2010-06-29T08:18:39.403 回答
0

嗯,我不完全理解你为什么将“确保”也放在默认的 c'tor 中。因为它调用了已经实现了完整合约的主 c'tor,所以默认 c'tor 也执行此操作 - 根据定义。所以这是一个逻辑冗余,因此是一个很大的“不要”。也许它可能会产生务实的影响,就像你说的 - 不知道代码合同那么好......

关于文学 - 最好的来源是:

  • 契约式设计,以身作则(书籍,Addison-Wesley)
  • 维基百科关于这个问题的文章
  • 合同设计简介(Eiffel 系列,但 DbC 原则不是特定于语言的)

!托马斯

于 2010-05-04T17:19:37.403 回答
0

契约式设计源于函数式编程的数学根源:前置条件后置条件。

你真的不需要一本关于它的书,它至多是计算机科学学位的一章(大多数会教授这个概念)。基本前提是您编写函数期望的先决条件以及在给定正确参数的情况下它将产生的输出。该函数预计不会使用不正确的初始参数。算法也可以这样说:它是绝对可靠的,即它保证提供预期的结果。

这就是我目前正在学习的学位中所教的方式,但可能有更好的定义。Wikipedia 关于按合同设计的文章是用 OO 倾斜的方式编写的,但前置/后置条件与语言无关。

于 2010-05-05T23:17:20.117 回答