One important aspect seems to be missing from the answers so far:
When an IEnumerable<T>
is returned to the caller, they must consider the possibility that the returned object is a “lazy stream”, e.g. a collection built with “yield return”. That is, the performance penalty for producing the elements of the IEnumerable<T>
may have to be paid by the caller, for each use of the IEnumerable. (The productivity tool “Resharper” actually points this out as a code smell.)
By contrast, an IReadOnlyCollection<T>
signals to the caller that there will be no lazy evaluation. (The Count
property, as opposed to the Count
extension method of IEnumerable<T>
(which is inherited by IReadOnlyCollection<T>
so it has the method as well), signals non-lazyness. And so does the fact that there seem to be no lazy implementations of IReadOnlyCollection.)
This is also valid for input parameters, as requesting an IReadOnlyCollection<T>
instead of IEnumerable<T>
signals that the method needs to iterate several times over the collection. Sure the method could create its own list from the IEnumerable<T>
and iterate over that, but as the caller may already have a loaded collection at hand it would make sense to take advantage of it whenever possible. If the caller only has an IEnumerable<T>
at hand, he only needs to add .ToArray()
or .ToList()
to the parameter.
What IReadOnlyCollection does not do is prevent the caller to cast to some other collection type. For such protection, one would have to use the class ReadOnlyCollection<T>
.
In summary, the only thing IReadOnlyCollection<T>
does relative to IEnumerable<T>
is add a Count
property and thus signal that no lazyness is involved.