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 likeLast()
andCount()
, whose semanticsIEnumerable<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 toIEnumerable<T>
: theCount
property. By promising a count, you assure your callers that yourIEnumerable<T>
really does have a terminus. They can then use LINQ extension methods likeLast()
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>
.
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().
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.
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.
“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.
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.
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