Reputation: 872
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;
}
}
Upvotes: 7
Views: 417
Reputation: 665
This may have been a problem in the CodeContract rewriter in the past. But the current version seems to do fine on your example. There's no issue here with iterators/delayed evaluation etc. The parameter i is captured by value and won't change during the iteration. Contracts should check this only at the beginning of the call to Test, not during each iteration.
Upvotes: 0
Reputation: 8455
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).
Upvotes: 0
Reputation: 241779
Here's a blog post related to this very subject concerning unit testing, iterators, delayed execution, and you.
Delayed execution is the issue here.
Upvotes: 0
Reputation: 14827
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.
Upvotes: 0
Reputation: 755397
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.
Upvotes: 2