I'm using the new Resharper version 6. In several places in my code it has underlined some text and warned me that there may be a Possible multiple enumeration of IEnumerable.
I understand what this means, and have taken the advice where appropriate, but in some cases I'm not sure it's actually a big deal.
Like in the following code:
var properties = Context.ObjectStateManager.GetObjectStateEntry(this).GetModifiedProperties(); if (properties.Contains("Property1") || properties.Contains("Property2") || properties.Contains("Property3")) { ... }
It's underlining each mention of properties
on the second line, warning that I am enumerating over this IEnumerable multiple times.
If I add .ToList()
to the end of line 1 (turning properties
from a IEnumerable<string>
to a List<string>
), the warnings go away.
But surely, if I convert it to a List, then it will enumerate over the entire IEnumerable to build the List in the first place, and then enumerate over the List as required to find the properties (i.e. 1 full enumeration, and 3 partial enumerations). Whereas in my original code, it is only doing the 3 partial enumerations.
Am I wrong? What is the best method here?
If the underlying type of the IEnumerable collection is an iterator-based implementation generated by LINQ methods like Select or yield in C# or yield statement in Visual Basic, you can fix the violation by converting and caching the collection to another type.
Deferred Danger. One of the many useful warnings in ReSharper is “Possible multiple enumeration of IEnumerable“. If you enumerate an enumerable more than once, ReSharper detects that and warns you about it. Although this warning may seem pointless at first, there are two good reasons to pay attention to it.
This kind of problem can be easily fixed — force the enumeration at the point of variable initialization by converting the sequence to an array or a list, for example: List<string> names = GetNames().
Enumerating an enumerable can be very expensive. For example, an enumerable might be backed by a database. Re-enumerating may force you to wait another network round trip. You don't want to pay that cost twice.
I don't know exactly what your properties
really is here - but if it's essentially representing an unmaterialized database query, then your if
statement will perform three queries.
I suspect it would be better to do:
string[] propertiesToFind = { "Property1", "Property2", "Property3" }; if (properties.Any(x => propertiesToFind.Contains(x)) { ... }
That will logically only iterate over the sequence once - and if there's a database query involved, it may well be able to just use a SQL "IN" clause to do it all in the database in a single query.
If you invoke Contains()
on a IEnumerable, it will invoke the extension method which will just iterate through the items in order to find it. IList
has real implementation for Contains()
that probably are more efficient than a regular iteration through the values (it might have a search tree with hashes?), hence it doesn't warn with IList
.
Since the extension method will only be aware that it's an IEnumerable
, it probably can not utilize any built-in methods for Contains()
even though it would be possible in theory to identify known types and cast them accordingly in order to utilize them.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With