Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using IReadOnlyCollection<T> instead of IEnumerable<T> for parameters to avoid possible multiple enumeration

My question is related to this one concerning the use of IEnumerable<T> vs IReadOnlyCollection<T>.

I too have always used IEnumerable<T> to expose collections as both return types and parameters because it benefits from being both immutable and lazily executed.

However, I am becoming increasingly concerned about the proliferation of places in my code where I must enumerate a parameter to avoid the possible multiple enumeration warning that ReSharper gives. I understand why ReSharper suggests this, and I agree with the code it suggests (below) in order to ensure encapsulation (i.e., no assumptions about the caller).

 Foo[] arr = col as Foo[] ?? col.ToArray();

However, I find the repetitiveness of this code pollutive, and I agree with some sources that IReadOnlyCollection<T> is a better alternative, particularly the points made in this article, which states:

Lately, I’ve been considering the merits and demerits of returning IEnumerable<T>.

On the plus side, it is about as minimal as an interface gets, so it leaves you as method author more flexibility than committing to a heavier alternative like IList<T> or (heaven forbid) an array.

However, as I outlined in the last post, an IEnumerable<T> return entices callers to violate the Liskov Substitution Principle. It’s too easy for them to use LINQ extension methods like Last() and Count(), whose semantics IEnumerable<T> does not promise.

What’s needed is a better way to lock down a returned collection without making such temptations so prominent. (I am reminded of Barney Fife learning this lesson the hard way.)

Enter IReadOnlyCollection<T>, new in .NET 4.5. It adds just one property to IEnumerable<T>: the Count property. By promising a count, you assure your callers that your IEnumerable<T> really does have a terminus. They can then use LINQ extension methods like Last() with a clear conscience.

However, as the observant may have noticed, this article only talks about using IReadOnlyCollection<T> for return types. My question is, would the same arguments equally apply to using it for parameters also? Any theoretical thoughts or comments on this would also be appreciated.

In fact, I'm thinking a general rule of thumb to use IReadOnlyCollection<T> would be where there would be possible multiple enumeration (vis-à-vis the ReSharper warning) if IEnumerable<T> is used. Otherwise, use IEnumerable<T>.

like image 374
Neo Avatar asked Dec 21 '16 13:12

Neo


People also ask

How do you fix multiple enumeration of IEnumerable?

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().

Should I pass IEnumerable or List?

IEnumerable is more efficient and faster when you only need to enumerate the data once. The List is more efficient when you need to enumerate the data multiple times because it already has all of it in memory.

What is IReadOnlyCollection C#?

The IReadOnlyCollection interface extends the IEnumerable interface and represents a basic read-only collection interface. It also includes a Count property apart from the IEnumerable members as shown in the code snippet given below. IReadOnlyCollection<Product> data = products; int numOfRecords = data.

Why is multiple enumeration bad?

“Possible multiple enumeration of IEnumerable“ is a resharper warning. If you enumerate an enumerable more than once, ReSharper will detect this and warn you about it. Although this warning may seem pointless at first, there are two good reasons to pay attention to it: Enumerating an enumerable can be very expensive.


1 Answers

Having thought about this further, I have come to the conclusion, based on the article I mentioned in my Question, that it is indeed OK to use IReadOnlyCollection<T> as a parameter, but only in functions where it will definitely be enumerated. If enumeration is conditional based on other parameters, object state, or workflow, then it should still be passed in as IEnumerable<T> so that lazy evaluation is semantically ensured.

like image 122
Neo Avatar answered Sep 20 '22 06:09

Neo