Just now find it by chance, Add(T) is defined in ICollection<T>
, instead of IEnumerable<T>
. And extension methods in Enumerable.cs don't contain Add(T), which I think is really weird. Since an object is enumerable, it must "looks like" a collection of items. Can anyone tell me why?
views:
198answers:
7As its name says, you can enumerate (loop) over an IEnumerable, and that's about it. When you want to be able to Add something to it, it wouldn't be just an enumerable anymore, since it has extra features.
For instance, an array is an IEnumerable, but an array has a fixed length, so you can't add new items to it.
IEnumerable is just the 'base' for all kind of collections (even readonly collections - which have obviously no Add() method). The more functionality you'd add to such 'base interface', the more specific it would be.
An IEnumerable<T>
is just a sequence of elements; see it as a forward only cursor. Because a lot of those sequences are generated values, streams of data, or record sets from a database, it makes no sense to Add
items to them.
An enumerable is exactly that - something you can enumerate over and discover all the items. It does not imply that you can add to it.
Being able to enumerate is universal to many types of objects. For example, it is shared by arrays and collections. But you can't 'add' to an array without messing about with it's structure - whereas a Collection is specifically built to be added to and removed from.
Technically you can 'add' to an enumerable, however - by using Concat<>
- however all this does is create an enumerator that enumerates from one enumerable to the next - giving the illusion of a single contigious set.
The name says it all. IEnumerable is for enumerating items only. ICollection is the actual collection of items and thus supports the Add method.
IEnumerable is designed to enumerate items, not to modify the underlying collection. Also, IEnumerable is the root class of LINQ. So it always indicates, that you can do LINQ Queries on it.
If add is really needed, this extension makes it possible, but should be used with care of the situation:
public static bool TryAdd<T>(this IEnumerable<T> enumeration, T newItem)
{
if (enumeration is ICollection<T>)
{
((ICollection<T>)enumeration).Add(newItem);
return true;
}
else
{
return false;
}
}
Edit: refactor to TryAdd
Each ICollection
should be IEnumerable
(I think, and the .NET Framework team seems to agree with me ;-)), but the other way around does not always make sense. There is a hierarchy of "collection like objects" in this world, and your assumption that an enumerable would be a collection you can add items to does not hold true in that hierarchy.
Example: a list of primary color names would be an IEnumerable
returning "Red"
, "Blue"
and "Green"
. It would make no logical sense at all to be able to do a primaryColors.Add("Bright Purple")
on a "collection" filled like this:
...whatever...
{
...
var primaryColors = EnumeratePrimaryColors();
...
}
private static IEnumerable<string> EnumeratePrimaryColors() {
yield return "Red";
yield return "Blue";
yield return "Green";
}