我正在为学校目的实现我自己的ArrayList,但为了调整一些东西,我正在尝试使用C#4.0代码契约。一切都很好,直到我需要将Contracts添加到构造函数中。我应该在空参数构造函数中添加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中。
此外,是否有任何书籍更深入地了解设计合同的原则和用法?有一件事是了解如何在一种语言中使用Contracts的语法(在本例中为C#),另一种是知道如何以及何时使用它。我阅读了几篇教程和Jon Skeet的C#深度文章,但如果可能的话,我想更深入一点。
由于
答案 0 :(得分:5)
我完全不同意托马斯的回答。只要您在ArrayList()
的实施中做出选择,您就应该签订一份合同来记录这些选择。
在这里,您可以选择使用参数32调用主构造函数。还有许多其他事情可以决定(不仅仅是关于默认大小的选择)。给ArrayList()
的合同几乎与ArrayList(int)
文件的合同相同,你决定不做大部分愚蠢的事情,而不是直接调用它。
答案“它调用主构造函数,所以让主构造函数的契约完成工作”完全忽略了这样一个事实,即合同可以帮助你避免查看实现。对于基于运行时断言检查的验证策略,即使对于几乎直接调用另一个构造函数/方法的这种简短构造函数/方法,写入契约的缺点是最终会检查两次。是的,这似乎是多余的,但运行时断言检查只是一种验证策略,而DbC的原则是独立的。原则是:如果可以调用它,它需要一份合同来记录它的作用。
答案 1 :(得分:1)
使用ArrayList
的客户端代码(使用代码合同)将不知道Ensure
的空构造函数Size == 32
,除非您使用Ensure
明确说明。
所以(例如):
var x = new ArrayList();
Contract.Assert(x.Size == 32)
会给你警告“断言未经证实”。
您需要明确说明所有合同;代码合同重写器/静态检查器不会“查看”方法以查看任何含义 - 请参阅my answer to the related question "Do we have to specify Contract.Requires(…) statements redundantly in delegating methods?"
答案 2 :(得分:1)
我建议您阅读Bertrand Meyer的Object Oriented Software Construction, 2nd Edition或Touch of Class。或者,您可以阅读同一作者的1992年文章Applying "Design by Contract"。
总结:
所以在你的情况下,专注于不变量。无论调用哪个构造函数,都会生成一个正确的对象(一个满足类不变量的对象)。
在此related answer中,我讨论了类似的主题,包括一个例子。
答案 3 :(得分:0)
嗯,我不完全理解为什么你把'保险'也放在默认的c'tor中。因为它调用了主要的c'tor,它已经实现了完整的合同,默认的c'tor也是如此 - 按照定义。所以这是一个逻辑冗余,因此是一个很大的“不要”。也许它可能具有实用意义,就像你说的那样 - 不知道代码合约那么好......
关于文学 - 最好的资料来源是:
HTH! 托马斯
答案 4 :(得分:0)
按合同设计来自函数式编程的数学根源:Preconditions和Postconditions。
你真的不需要一本书,它至多是计算机科学学位的一章(大部分都会教授这个概念)。基本前提是您编写函数所需的前提条件以及在给定正确参数的情况下将生成的输出。预计该函数不会使用不正确的初始参数。对于算法也可以这样说:它是绝对可靠的,即保证提供预期的结果。
这就是我在目前正在学习的程度上的教学方式,但可能有更好的定义。关于按合同设计的维基百科文章是用OO倾斜编写的,但前/后条件与语言无关。