我正在尝试在代码契约中演示不变量,我想我会给出一个排序的字符串列表示例。它在内部维护一个数组,有额外的空间用于添加等 - 基本上就像List<T>
一样。当它需要添加一个项目时,它会将它插入到数组中,等等。我想我有三个不变量:
现在,我试图以这种方式实现它:
[ContractInvariantMethod]
private void ObjectInvariant()
{
Contract.Invariant(count >= 0 && count <= buffer.Length);
for (int i = count; i < buffer.Length; i++)
{
Contract.Invariant(buffer[i] == null);
}
for (int i = 1; i < count; i++)
{
Contract.Invariant(string.Compare(buffer[i], buffer[i - 1]) >= 0);
}
}
不幸的是,ccrewrite
搞乱了循环。
用户文档说该方法应该是对Contract.Invariant
的一系列调用。我是否真的必须像这样重写代码?
Contract.Invariant(count >= 0 && count <= buffer.Length);
Contract.Invariant(Contract.ForAll
(count, buffer.Length, i => buffer[i] == null));
Contract.Invariant(Contract.ForAll
(1, count, i => string.Compare(buffer[i], buffer[i - 1]) >= 0));
这有点难看,虽然它确实有效。 (这比我以前的尝试要好得多,请注意。)
我的期望是否不合理?我的不变量是不合理的吗?
(也以question in the Code Contracts forum的形式提问。我将在此自行添加相关答案。)
答案 0 :(得分:8)
从(初步)MSDN页面看,Contract.ForAll成员可以帮助您完成2个范围合同。虽然文档的功能不是很明确。
//untested
Contract.Invariant(Contract.ForAll(count, buffer.Length, i => buffer[i] == null));
Contract.Invariant(Contract.ForAll(1, count,
i => string.Compare(buffer[i], buffer[i - 1]) >= 0));
答案 1 :(得分:3)
(我将接受Henk的回答,但我认为值得加上这个。)
问题现在已在MSDN forum上得到解答,结果是第一种形式不是预期可行。不变量确实需要对Contract.Invariant
进行一系列调用,而这就是全部。
这使静态检查器更容易理解不变量并强制执行它。
通过简单地将所有逻辑放入不同的成员,例如,可以避免这种限制。 IsValid
属性,然后调用:
Contract.Invariant(IsValid);
毫无疑问,这会破坏静态检查程序,但在某些情况下,在某些情况下它可能是一个有用的替代方案。
答案 2 :(得分:1)
设计师不是要重新发明轮子吗?
出了什么问题bool Invariant() const; // in C++, mimicking Eiffel
现在在C#中我们没有const,但为什么你不能只定义一个Invariant
函数
private bool Invariant()
{
// All the logic, function returns true if object is valid i.e. function
// simply will never return false, in the absence of a bug
}
// Good old invariant in C#, no special attributes, just a function
然后根据该功能使用代码合约?
[ContractInvariantMethod]
private void ObjectInvariant()
{
Contract.Invariant(Invariant() == true);
}
也许我在写废话,但即使在这种情况下,当每个人都告诉我错误时,它会有一些教学价值。