Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IEnumerable multiple enumeration caused by contract precondition

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?

like image 682
Keith Avatar asked Jul 04 '12 10:07

Keith


People also ask

Why can't IEnumerable be used as a parameter?

The problem with taking IEnumerable as a parameter is that it tells callers "I wish to enumerate this". It doesn't tell them how many times you wish to enumerate. I can change the objects parameter to be List and then avoid the possible multiple enumeration but then I don't get the highest object that I can handle.

Why can't I enumerate multiple times in an enumerable?

The issue is that the consumer of the enumerable isn't necessarily the owner of it, and so doesn't know if it's safe to enumerate multiple times. If it is safe, or if the method has a requirement to be able to enumerate multiple times, it might be more appropriate to accept an IList or IReadOnlyList as the parameter to the API.

Is it possible to convert an IEnumerable to a list?

Making ever user of an IEnumerable convert it to a list seems like a bad idea. The issue is that the consumer of the enumerable isn't necessarily the owner of it, and so doesn't know if it's safe to enumerate multiple times.

What does it mean when an IEnumerable is evaluated twice?

It just means the IEnumerable is evaluated twice, wich is usually not a problem unless the evaluation itself takes a long time. Even if it does take a long time, in this case your only using one element the first time around. In this scenario you could also exploit the powerful linq extension methods even more.


1 Answers

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));
like image 181
Damien_The_Unbeliever Avatar answered Sep 27 '22 22:09

Damien_The_Unbeliever