3

I ended up in this post while searching for solutions to my problem - which led me to propose a new answer there - and - to be confronted with the following question:

Considering ICollection implements IEnumerable, and all linq extensions apply to both interfaces, is there any scenario where I would benefit from working with an IEnumerable instead of an ICollection ?

The non generic IEnumerable, for instance, does not provide a Count extension. Both ICollection interfaces do.

Given all ICollection, in any case, provide all functionality IEnumerable implement - since it itself implements it - why then would I opt for IEnumerable in place of ICollection ?

Backward compatibility with previous frameworks where ICollection was not available ?

Community
  • 1
  • 1
Veverke
  • 9,208
  • 4
  • 51
  • 95
  • 3
    `IEnumerable` is a parent interface of `ICollection`. So if your method only takes `ICollection` it's not as re-usable as if it accepted `IEnumerable`. Also note that many LINQ extension methods try to cast to `IList` or `ICollection` to use the property instead of enumerating the sequence. `Enumerable.Count` is optimized as you can see [here](http://referencesource.microsoft.com/#System.Core/System/Linq/Enumerable.cs,41ef9e39e54d0d0b,references). – Tim Schmelter Nov 17 '15 at 10:43
  • @TimSchmelter: thanks Tim. The statement **So if you say that your method only take ICollection it's not as re-usable as if you would take IEnumerable** however does answer "why not use A instead of B, if A is B+". If by whatever circumstances IEnumerable is more widely used - it is still not a reason why not to be using ICollection, given ICollection is an IEnumerable (unless the above is wrong). – Veverke Nov 17 '15 at 10:48
  • @Veverke unless there's a specific reason not to, you should always prefer the higher class in the hierarchy that has whatever you need. This gives more flexibility: if there's nothing specific in `ICollection` that you need, using `IEnumerable` gives more flexibility. – Jcl Nov 17 '15 at 10:54
  • @Jcl: I agree and it's a strong point. However, as with my comment on one of Tim's statements above - I still think this *per se* does not answer the question. Luk's answer, below, on other hand, brings features that IEnumerable has over ICollection. IEnumerable may be way more frequent everywhere - but if I do not care about portability, then it does not even touch the question's point. – Veverke Nov 17 '15 at 11:10
  • @Veverke `IEnumerable` doesn't have any feature over `ICollection`. Since it's a more simple contract, if anything, it has *less* features: `ICollection` has everything `IEnumerable` has (it implements `IEnumerable`), and then some more things. Also, I don't really see what portability has to do with all this. I think you are either mixing things here, or I'm not understanding you at all. – Jcl Nov 17 '15 at 11:13
  • @Jcl: I am the one who is saying that ICollection has features over IEnumerable, not the contrary - hence the question. I meant, in the commentary above, that unless one does not show what IEnumerable has in addition to offer over ICollection, the question will remains unanswered. As for *portability* - I may have employed the wrong word - I meant compatibility. For reusability I do not see a difference in having the whole set of utilities one writes using ICollection. Unless, again, someone (like the proposed answer, by luk32, does) shows things ICollection lacks in comparison to IEnumerable. – Veverke Nov 17 '15 at 11:32

3 Answers3

4

IEnumerable provides a read-only interface to a collection and ICollection allows modification. Also IEnumerable needs just to know how to iterate over elements. ICollection has to provide more information.

This is semantically different. You don't always want to provide a functionality for modification of a collection.

There is a IReadOnlyCollection but it doesn't implement ICollection. This is a design of C#, that ReadOnly is a different stripped down interface.

The point made by Tim is quite important. The internal working for Count might be dramatically different. IEnumerable does not need to know how many elements it spans over. Collection has a Property, so it has to know how many elements it contains. That is another crucial difference.

luk32
  • 15,812
  • 38
  • 62
  • Oh, here is a difference. Thanks ! – Veverke Nov 17 '15 at 10:49
  • Notice that this is true for the generic `ICollection`, but not for the non-generic `ICollection`. For the non-generic version, the only difference against an `IEnumerable` is the syncing (having a sync root object) and the direct count. – Jcl Nov 17 '15 at 10:52
  • Oh yeah I am a pacifist so I didn't touch non-generics at all, as I dislike boxing. I should probably expand the answer. – luk32 Nov 17 '15 at 10:55
4

I think there are actually two questions to answer here.

When would I want IEnumerable<T>?

Unlike other collection types and the language in general, queries on IEnumerables are executed using lazy evaluation. That means you can potentially perform several related queries in only enumeration.

It's worth noting that lazy evaluation doesn't interact nicely with side effects because multiple enumeration could then give different results, you need to keep this in mind when using it. In a language like C#, lazy evaluation can be a very powerful tool but also a source of unexplained behaviour if you aren't careful.

When would I not want ICollection<T>?

ICollection<T> is a mutable interface, it exposes, amongst other things, add and remove methods. Unless you want external things to be mutating your object's contents, you don't want to be returning it. Likewise, you generally don't want to be passing it in as an argument for the same reason.

If you do want explicit mutability of the collection, by all means use ICollection<T>.

Additionally, unlike IEnumerable<T> or IReadOnlyCollection<T>, ICollection<T> is not covariant which reduces the flexibility of the type in certain use cases.

Non-generic versions

Things change a bit when it comes to the non-generic versions of these interfaces. In this case, the only real difference between the two is the lazy evaluation offered by IEnumerable and the eager evaluation of ICollection.

I personally would tend to avoid the non-generic versions due to the lack of type safety and poor performance from boxing/unboxing in the case of value types.

Summary

If you want lazy evaluation, use IEnumerable<T>. For eager evaluation and immutability use IReadOnlyCollection<T>. For explicit mutability use ICollection<T>.

TheInnerLight
  • 12,034
  • 1
  • 29
  • 52
  • Thanks for adding the mutability aspect of ICollection - and the absence of it, in IEnumerable. – Veverke Nov 17 '15 at 11:35
2

The idea is to use the simplest contract (interface) which fulfills the requirements: ICollection is a collection, IEnumerable is a sequence. A sequence could have deferred execution, it could be infinite, etc. The interface IEnumerable just tells you that you can enumerate the sequence, that is all. This is different from ICollection, which represents an actual collection containing a finite number of items.

As you can see, these are quite different. You cannot ignore the semantics of these contracts, and just focus on which interface inherits which other one.

If your algorithm only involves enumeration of the input data, then it should take an IEnumerable. If you are, by contract, dealing with collections (i.e, you expect collections and nothing else), then you should use ICollection.

odyss-jii
  • 2,619
  • 15
  • 21
  • Could a sequence really be *infinite* ? I think infinity is an abstract concept with no real implementation that keeps its *infinite* nature intact. If we hold whatever sequence at hand at any given time, it is not finite anymore, because I am able to enumerate it. – Veverke Nov 17 '15 at 11:33
  • 1
    @Veverke It is possible to have an infinite `IEnumerable` yes, all we need from an `IEnumerable` is an enumerator that describe how to move to the next element. That description of how to `MoveNext()` could extend to infinity. You cannot, of course, evaluate an infinite `IEnumerable` but you can evaluate a subset of it and that can, in some cases, be a useful approach. – TheInnerLight Nov 17 '15 at 11:46
  • @Veverke: the contract only requires the implementation to provide the next value when requested. Contrary to what you wrote, you do not "hold the sequence at hand". Here is an infinite sequence: `IEnumerable InfiniteSequence() { while(true) yield return 0; }`. Try enumerating that sequence. If you want practical applications: providing live sensor-readings in a sequence. – odyss-jii Nov 17 '15 at 12:44