我正在尝试使用代码契约来设置一个不可变的有序单链表列表来强制执行排序。我遇到了一些问题,可以归结为这个例子:
[Pure, ContractVerification(true)]
public class Hierarchy
{
private readonly object _data;
private readonly Hierarchy _childField;
public Hierarchy() { }
public Hierarchy(object data, Hierarchy childParameter) {
Contract.Requires<ArgumentNullException>(childParameter != null);
_data = data;
_childField = childParameter;
Contract.Assert(HasChild);
Contract.Assert(_childField == childParameter);
Contract.Assert(_childField.Data == childParameter.Data);
Contract.Assert(ChildProperty == _childField);
Contract.Assert(ChildProperty.Data == _childField.Data); //Warning -- CodeContracts: assert unproven
}
public bool HasChild { get { return _childField != null; } }
public object Data {
get {
Contract.Ensures(Contract.Result<object>() == _data);
return _data;
}
}
public Hierarchy ChildProperty {
get {
Contract.Requires<InvalidOperationException>(HasChild);
Contract.Ensures(Contract.Result<Hierarchy>() == _childField);
//un-commenting this line causes the assertion to succeed.
//Contract.Ensures(Contract.Result<Hierarchy>().Data == _childField.Data);
return _childField;
}
}
[ContractInvariantMethod]
private void Invariant() {
Contract.Invariant(HasChild == (ChildProperty != null));
}
}
在我看来,代码合同应该能够证明断言ChildProperty.Data == _childField.Data
。先前的断言ChildProperty == _childField
成功,并且在同一对象上调用纯Data
属性getter两次应返回相同的结果。
还注意_childField == childParameter
和_childField.Data == childParameter.Data
的早期断言(成功)。
如上面第二条评论中所述,通过将后置条件Contract.Ensures(Contract.Result<Hierarchy>().Data == _child.Data);
添加到ChildProperty
getter来解决问题。请注意,这就是我们需要做的全部 - 在这种情况下,代码合同会识别出x == y
时,在x和y上调用Data
getter会产生相同的结果。
对于这个小例子,这个解决方法很好,但是对于一个大类来说,必须为每个类型的属性添加一个后置条件是很麻烦的(更不用说有点傻了)。
这是一个错误吗?我是否错过了一些合同注释或其他可以证明断言Child.Data == _child.Data
的东西?换句话说,还有其他方法可以解决这个问题吗?
答案 0 :(得分:3)
你是对的。这应该是可证明的,但不是由于静态检查器的限制。
答案 1 :(得分:0)
可能是因为Data
属性不保证不更改_data
的内容(虽然它是readonly
,但其内容仍然可能是可变的)?
例如,假设你有这个:
public object Data {
get {
Contract.Ensures(Contract.Result<object>() == _data);
((SomeClass)_data).SomeProperty += 1;
return _data;
}
}
然后对Data
的两次调用会得到不同的结果。
将此添加到Data
的{{1}}是否有效?
get