views:

446

answers:

3

I'm trying to demonstrate invariants in Code Contracts, and I thought I'd give an example of a sorted list of strings. It maintains an array internally, with spare space for additions etc - just like List<T>, basically. When it needs to add an item, it inserts it into the array, etc. I figured I had three invariants:

  • The count must be sensible: non-negative and at most as big as the buffer size
  • Everything in the unused part of the buffer should be null
  • Each item in the used part of the buffer should be at least as "big" as the item before it

Now, I've tried to implement that in this way:

[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);
    }
}

Unfortunately, ccrewrite is messing up the loops.

The user documentation says that the method should just be a series of calls to Contract.Invariant. Do I really have to rewrite the code as something like this?

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));

That's somewhat ugly, although it does work. (It's much better than my previous attempt, mind you.)

Are my expectations unreasonable? Are my invariants unreasonable?

(Also asked as a question in the Code Contracts forum. I'll add any relevant answers here myself.)

+2  A: 

From the (preliminary) MSDN pages it looks like the Contract.ForAll member could help you with the 2 range contracts. The documentation isn't very explicit about it's function though.

//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));
Henk Holterman
I could use ForAll for the first one (if that's allowed in invariants) but the second one needs two elements at a time, which is somewhat trickier.
Jon Skeet
Actually, it might be better than that. Hmm..
Jon Skeet
There are two version, one is integer-range based and takes a Predicate<int>. I added a sample of how it could work.
Henk Holterman
Thanks very much for this Henk - it's definitely looking better than it did. I've used buffer.Skip() with the normal ForAll for the first one, but we're clearly thinking alike for the second :)
Jon Skeet
Actually, now I look at it, I think I like your version better all round, as it's closer to the initial code. Editing again. I won't accept the answer *just yet* as I'd still like to hear whether it's expected that my initial code won't work.
Jon Skeet
I found in an older posting that Contract.ForAll was not yet supported (static) last april: http://social.msdn.microsoft.com/Forums/en-US/codecontracts/thread/94ec2bce-61b5-463c-a1b6-22c22a42e67f
Henk Holterman
Fortunately that doesn't affect me yet as I don't have VSTS :)
Jon Skeet
A: 

(I'm going to accept Henk's answer, but I think it's worth adding this.)

The question has now been answered on the MSDN forum, and the upshot is that the first form isn't expected to work. Invariants really, really need to be a series of calls to Contract.Invariant, and that's all.

This makes it more feasible for the static checker to understand the invariant and enforce it.

This restriction can be circumvented by simply putting all the logic into a different member, e.g. an IsValid property, and then calling:

Contract.Invariant(IsValid);

That would no doubt mess up the static checker, but in some cases it may be a useful alternative in some cases.

Jon Skeet
Pretty much what I suggested. I would adopt this as an "effective" pattern when using .Net Code Contracts.
Daniel Daranas
+1  A: 

Aren't the designers reinventing the wheel a bit?

What was wrong with the good old

bool Invariant() const; // in C++, mimicking Eiffel

?

Now in C# we don't have const, but why can't you just define an Invariant function

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

and then just use the Code Contracts in terms of that function?

[ContractInvariantMethod]
private void ObjectInvariant()
{
    Contract.Invariant(Invariant() == true);
}

Maybe I'm writing nonsense, but even in that case it will have some didactic value when everybody tells me wrong.

Daniel Daranas
You *can* do that - but then the static checker has almost no information to work with to make sure that the invariant remains intact. Where possible it's better to use a set of calls to Contract.Invariant.
Jon Skeet
@Jon I see. Well, traditionally the invariant was not statically checked, only called before and after any public function when running with assertion checking on. I understand that Code Contracts try to introduce static analysis value. I still have to learn how that works in practice, though.
Daniel Daranas