我与同事讨论过使用代码合同来执行先决条件检查。
假设我们有以下代码:
namespace Project
{
using System;
using System.Diagnostics.Contracts;
public class Thing
{
public string Foo { get; set; }
public int Bar { get; set; }
}
public class ThingsManipulator
{
public void AddThing(Thing thing)
{
Contract.Requires<ArgumentNullException>(thing != null);
// Do something
}
}
}
如果// Do something
我正在访问thing.Foo
和thing.Bar
做某事,我是否应该通过代码合同对其进行验证?
public void AddThing(Thing thing)
{
Contract.Requires<ArgumentNullException>(thing != null);
Contract.Requires<ArgumentException>(!string.IsNullOrWhiteSpace(thing.Foo));
Contract.Requires<ArgumentException>(thing.Bar > 0);
// Do something
}
我的同事说只应检查整个参数(即我们应该只放置第一份合约),我认为无论是整个参数还是其中一个属性,方法都应检查它们的用途(即我们应该所有三个合同)。
请注意,我理解并同意,如果参数的属性应始终满足要求,则该要求应放在对象的不变检查中。
我所指的是通常有效但对特定方法无效的值(例如,在上面的示例中thing.Bar
可能会很乐意保留负值,但AddThing
不喜欢它们)。
我的同事说在这些情况下,方法签名应该显示它使用的所有项目而不是单个对象(例如AddThing(string thingFoo, int thingBar)
),并对它们运行检查。
所以:
我无法在the manual找到相关指南,也许我错过了什么?
答案 0 :(得分:6)
有助于将合同视为不是验证事物,而是将验证者试图证明的事实陈述。你实质上在说“只要我的输入是正确的( Contract.Requires ),那么以下属性就会成立( Contract.Ensures , Contract.Assert , Contract.Invariant )“。
合同应该是关于它们所使用的上下文应该始终是真实的陈述。在你的例子中,你说的是Thing的Foo必须是非空的仅 '正在被一个ThingsManipulator使用。
如果你可以说Thing的Foo 总是需要非空,那么对于Thing来说总是如此,并且契约属于Thing。
在这种情况下你不能,所以我认为这成为关于参数的OO设计问题。大声思考:
如果(3)停止为真,则可能值得创建一个装饰或以其他方式从Thing派生的新类型,并将合同放在那里,以避免repeating yourself的问题。但目前,YAGNI。