12

I have an IEnumerable parameter that is required to be non-empty. If there's a precondition like the one below then the collection will be enumerated during it. But it will be enumerated again the next time I reference it, thereby causing a "Possible multiple enumeration of IEnumerable" warning in Resharper.

void ProcessOrders(IEnumerable<int> orderIds)
{
    Contract.Requires((orderIds != null) && orderIds.Any());  // enumerates the collection

    // BAD: collection enumerated again
    foreach (var i in orderIds) { /* ... */ }
}

These workarounds made Resharper happy but wouldn't compile:

// enumerating before the precondition causes error "Malformed contract. Found Requires 
orderIds = orderIds.ToList();
Contract.Requires((orderIds != null) && orderIds.Any());
---
// enumerating during the precondition causes the same error
Contract.Requires((orderIds != null) && (orderIds = orderIds.ToList()).Any());

There are other workarounds that would be valid but maybe not always ideal like using ICollection or IList, or performing a typical if-null-throw-exception.

Is there a solution that works with code contracts and IEnumerables like in the original example? If not then has someone developed a good pattern for working around it?

Keith
  • 20,636
  • 11
  • 84
  • 125
  • 3
    I think it's probably just a bad idea to have a contract dependant on an IEnumerable - as IEnumerables by definition can incur side effects. – Dave Bish Jul 04 '12 at 10:36
  • So far I've used ICollection as a workaround and never had a problem, though I'm curious if there's a solution for IEnumerables. – Keith Jul 04 '12 at 10:49

1 Answers1

8

Use one of the methods designed to work with IEnumerables, such as Contract.Exists:

Determines whether an element within a collection of elements exists within a function.

Returns

true if and only if predicate returns true for any element of type T in collection.

So your predicate could just return true.


Contract.Requires(orderIds != null);
Contract.Requires(Contract.Exists(orderIds,a=>true));
Community
  • 1
  • 1
Damien_The_Unbeliever
  • 234,701
  • 27
  • 340
  • 448
  • 2
    Will this not also enumerate the `IEnumerable`? – Rawling Jul 04 '12 at 10:37
  • 1
    Only if you a) Have enabled Runtime checking, and b) Haven't selected "Skip Quantifiers". (Although, in such a case, I'd recommend splitting into two `Requires`) – Damien_The_Unbeliever Jul 04 '12 at 10:45
  • Ah OK. So would it be correct to say that neither the original code nor your code _actually_ cause an enumeration (modulo your a and b above), but ReSharper doesn't realise this in the original code, and your code just puts it in a form that RS ignores? – Rawling Jul 04 '12 at 10:52
  • 1
    @Rawling - correct - without the runtime checking, all of the code inside calls to `Contract` methods is removed. So, it's just a way to hide it better from Resharper. – Damien_The_Unbeliever Jul 04 '12 at 10:53
  • Thanks for the answer. However Resharper still complains whether runtime checking is on or not. Any idea how to get Resharper to happily accept this? I had already added an annotations file for code contracts per [these instructions](http://www.infinitec.de/post/2010/11/18/Using-Code-Contracts-Visual-Studio-and-with-Resharper.aspx) which clears up a lot of contract issues with RS but apparently it doesn't cover Contract.Exists. – Keith Jul 05 '12 at 18:38
  • @Keith - I think you might have to take it up with the RS developers at this point - it's quite contextual though, so may be difficult to analyze. – Damien_The_Unbeliever Jul 05 '12 at 18:41