views:

198

answers:

7

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?

+8  A: 

IEnumerable is for reading, not for writing.

Brian
+2  A: 

As 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.

Frederik Gheysels
+22  A: 

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.

Steven
+1 for using the word *sequence*, since IEnumerable is exactly that, and not some kind of collection.
OregonGhost
+8  A: 

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.

Andras Zoltan
+1 for suggesting an alternative, the Concat.
Emile
+1  A: 

The name says it all. IEnumerable is for enumerating items only. ICollection is the actual collection of items and thus supports the Add method.

korchev
A: 

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

JanW
So if it's not an `ICollection<T>`, you just silently return the input, without the new item?
Matthew Flaschen
I agree with Matthew. You'd be better throwing an exception, but even better: Don't do this. You are making code that fails @ runtime.
Steven
This is bound to result in very unintuitive code. At least if you _really_ want to (which I strongly argue against since that's basically what ICollection is for) at least write a method that can handle the request. E.g. ... else {var temp = new List<T>(enumeration); temp.Add(newItem); return temp;} which might be _extremely_ slow so again I do not recommend a method like the preposed
Rune FS
Oh my god...i just wanted to give Danny a hint how to to what he intends. Of course what i wrote is only a basic example how to realize it. Added the exception, which is of course sensefull.
JanW
@JanW - He didn't ask *how* to implement `Add` on `IEnumerable`, he asked *why* it wasn't implemented, which has been well answered. Your answer gives a good way to write code that compiles but fails at runtime which is not a good thing, hence all the down-votes. Having it throw an exception doesn't fix it, it just makes it wrong in a more obvious manner. If you are conditionally casting an `IEnumerable<T>` to an `ICollection<T>` and calling `Add` for some godforsaken reason, then it should not be abstracted into a method; it should be an ugly inline code wart that is plain to see.
Greg Beech
@Greg Beech - if you read my answer and did not only see all the down votes and just add your additional downvote, you will see, that I first explained him why Add is not found on IEnumerable, as all the others did. In addition, I added the code, to give him a chance to do it. If you only work with collections in your code, the extension is infact sensefull. Since a programmer should know what he does, it does not harm anything, if you have this extension in a class, in which you often need the functionality.
JanW
@JanW - You can probably see from the number of down-votes that people don't agree with you. This method is a bad idea.
Greg Beech
@Greg Beech - it is obivous, that you dont get the point. I also would not recommend this if you are an unexperienced c# developer. I maybe should refactor it to bool TryAdd<T>(...) to fit the mass. I still dont see that basically this method is a bad idea. I have faced lots of scenarios, in which it is very usefull. Almost all downvotes came with the first version without else case, which i agree to, because the user got no feedback if the operation was not successfull.
JanW
+3  A: 

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";
}
peSHIr
`ICollection` inherits from `IEnumerable` and `ICollection<T>` inherits from `IEnumerable<T>` (which inherits from `IEnumerable`). In other words you are right: Each `ICollection` is a `IEnumerable`.
Steven
@Steven: I know...
peSHIr