views:

145

answers:

4

The following code fails on the pre condition. Is this a bug in code contracts?

static class Program
{
    static void Main()
    {
        foreach (var s in Test(3))
        {
            Console.WriteLine(s);
        }
    }

    static IEnumerable<int>Test (int i)
    {
        Contract.Requires(i > 0);
        for (int j = 0; j < i; j++)
            yield return j;
    }
}
+1  A: 

My guess is this has to do with the delayed nature of iterators. Remember, contract processing will occur on the final emitted IL, not the C# code. This means you have to consider the generated code for features like iterators and lambda expressions.

If you decompile that code you'll find that "i" is not actually a parameter. It will be a variable in the class which is used to implement the iterator. So the code actually looks more like the following

class IteratorImpl {
  private int i;
  public bool MoveNext() {
    Contract.Require(i >0);
    ..
  }
}

I'm not terribly familiar with the contract API but my guess is the generated code is much harder to verify.

JaredPar
Why should the Requires be on the MoveNext instead of the contructor of IteratorImpl?
pn
@pn, this is just how the C# team chose to implement iterators. Any code which appears in the body of an iterator will end up in the MoveNext method of the generated code.
JaredPar
My question is whether this is a bug in code contracts or not. It looks like the code contract re writer does not understand iterators.
pn
@pn, I would suspect it's that the author doesn't understand the impact of iterators on code contracts
JaredPar
A: 

Remember that iterators aren't run until they are enumerated, and are compiled into some special sauce in the back end. The general pattern you should follow if you want to validate parameters, and this probably holds true for contracts, is to have a wrapper function:

static IEnumerable<int> Test (int i)
{
    Contract.Requires(i > 0);
    return _Test(i);
}

private static IEnumerable<int> _Test (int i)
{
    for (int j = 0; j < i; j++)
        yield return j;
}

That way Test() will do the checking of the parameters when it is called then return _Test(), which actually just returns a new class.

Talljoe
This is workaround. However, should I change my code or is this a bug that will be fixed?
pn
This is the way that iterators work -- C# isn't going to change this behavior. If you need to check the parameters of the method, and want to do it at invocation not at enumeration, then you have to wrap it in a second method that does the checking. I don't know about Contracts or whether they will fix themselves to work with iterators better.
Talljoe
A: 

Here's a blog post related to this very subject concerning unit testing, iterators, delayed execution, and you.

Delayed execution is the issue here.

Jason
A: 

This code will work with final version of .NET 4.0 (just tried it) where Code Contracts in interators are supported, but as I found out recently it does not always work properly (read more here).

jarek