8

I have code that looks something like this:

public class Foo<T> : ObservableCollection<T>
{
    private T bar;

    public Foo(IEnumerable<T> items)
        : base(items.ToList())
    {
        Contract.Requires(items != null);

        if (this.Any())
            this.bar = this[0]; // gives 'requires unproven: index < @this.Count'
    }
}

Shouldn't the Any check account for index 0? Am I doing something wrong, or does CodeContracts just not recognize this case?

Matthew
  • 28,056
  • 26
  • 104
  • 170

2 Answers2

6

LINQ's .Any and the item accessor [0] are unrelated enough that Code Contracts hasn't been made to consider them to be the same thing. Since this.bar would be initialized using a default value anyway, it's probably best to just do this:

Contract.Requires(items != null);
this.bar = items.FirstOrDefault();

This would not only resolve the possibility of thread-safety which AakashM points out, but it is also slightly more performant. Since you know this is a collection (and therefore has a .Count), another option would be:

if(this.Count > 0)
    this.bar = this[0]; 
StriplingWarrior
  • 151,543
  • 27
  • 246
  • 315
  • Ah, that makes sense. I guess I could even skip the null-check if I'm using `FirstOrDefault`, since `bar` will initially have its default value anyway. – Matthew Jul 28 '11 at 18:32
  • 1
    @Matthew: Yup, which makes for clearer, more concise code anyway! – StriplingWarrior Jul 28 '11 at 18:34
  • 1
    The first code snippet is incorrect whenever T is a value type – JaredPar Jul 28 '11 at 18:36
  • 1
    @StriplingWarrior the updated code is still incorrect. Consider the case where T is `int` and the first value is 0. It will consider the collection empty. Additionally I'm fairly certain that won't compile because you can't do equality comparisons on generic values – JaredPar Jul 28 '11 at 18:42
  • @StiplingWarrior, i verified the first sample no longer compiles – JaredPar Jul 28 '11 at 19:10
  • @Matthew: a key difference is that `.First()` will throw if the collection is empty, whereas `.FirstOrDefault()` won't. Which behaviour you need depends on your situation. – porges Jul 29 '11 at 02:47
  • @Porges: Right, originally I was avoiding that issue by ensuring that the collection wasn't empty. But since I want the default when the collection is empty, I think `FirstOrDefault` is a much better way of solving the same problem. – Matthew Jul 29 '11 at 13:11
6

None of the LINQ methods are annotated with the Contracts API. Hence when the verifier runs on this method it acquires no new data about the value of Count. This is why you see the warning.

One way to work around this is to use Assume to tell the verifier the count is valid at this point.

if (this.Any()) {
  Contract.Assume(this.Count > 0);
  this.bar = this[0];
}
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454