views:

4730

answers:

6

My question as title above. For example,

IEnumerable<T> items = new T[]{new T("msg")};
items.ToList().Add(new T("msg2"));

but after all it only has 1 item inside.

Can we have a method like items.Add(item)?

like the List<T>

+2  A: 

No the IEnumerable doens't support adding items to it.

An 'alternative' you have is.

var List = new List(items);
List.Add(otherItem);
pb
Your suggestion is not the only alternative. For example, ICollection and IList are both reasonable options here.
Jason
But you can't instantiate either.
pb
Of course you can't instantiate either - they're interfaces.
Mike C.
+10  A: 

The type IEnumerable<T> does not support such operations. The purpose of the IEnumerable<T> interface is to allow a consumer to view the contents of a collection. Not to modify the values.

When you do operations like .ToList().Add() you are creating a new List<T> and adding a value to that list. It has no connection to the original list.

What you can do is use the Add extension method to create a new IEnumerable<T> with the added value.

items = items.Add("msg2");

Even in this case it won't modify the original IEnumerable<T> object. This can be verified by holding a reference to it. For example

var items = new string[]{"foo"};
var temp = items;
items = items.Add("bar");

After this set of operations the variable temp will still only reference an enumerable with a single element "foo" in the set of values while items will reference a different enumerable with values "foo" and "bar".

EDIT

I contstantly forget that Add is not a typical extension method on IEnumerable<T> because it's one of the first ones that I end up defining. Here it is

public static IEnumerable<T> Add<T>(this IEnumerable<T> e, T value) {
  foreach ( var cur in e) {
    yield return cur;
  }
  yield return value;
}
JaredPar
That's called `Concat` :)
Pavel Minaev
@Pavel, thanks for pointing that out. Even Concat won't work though because it takes another IEnumerable<T>. I pasted the typical extension method I define in my projects.
JaredPar
I wouldn't call that `Add` though, because `Add` on virtually any other .NET type (not just collections) mutates the collection in-place. Maybe `With`? Or it could even be just another overload of `Concat`.
Pavel Minaev
@Pavel, I've gone back and forth over this a few times. I ended up sticking with Add most of the time because it's simply the first thing that comes to my mind. I rarely find it confusing with mutating Adds.
JaredPar
I had created this static entension before I asked the answer. I thought the IEnumerable is a collection can be modifed but its intend is view only, right?
ldsenow
+1  A: 

Not only can you not add items like you state, but if you add an item to a List<T> (or pretty much any other non-read only collection) that you have an existing enumerator for, the enumerator is invalidated (throws InvalidOperationException from then on).

If you are aggregating results from some type of data query, you can use the Concat extension method:

Edit: I originally used the Union extension in the example, which is not really correct. My application uses it extensively to make sure overlapping queries don't duplicate results.

IEnumerable<T> itemsA = ...;
IEnumerable<T> itemsB = ...;
IEnumerable<T> itemsC = ...;
return itemsA.Concat(itemsB).Concat(itemsC);
280Z28
+10  A: 

You cannot, because IEnumerable<T> does not necessarily represent a collection to which an items can be added. In fact, it does not necessarily represent a collection at all! For example:

IEnumerable<string> ReadLines()
{
     string s;
     do
     {
          s = Console.ReadLine();
          yield return s;
     } while (s != "");
}

IEnumerable<string> lines = ReadLines();
lines.Add("foo") // so what would this supposed to do??

What you can do, however, is create a new IEnumerable object (of unspecified type), which, when enumerated, will provide all items of the old one, plus some of your own. You use Enumerable.Concat for that:

 items = items.Concat(new[] { "foo" });

This will not change the array object (you cannot insert items into to arrays, anyway). But it will create a new object that will list all items in the array, and then "Foo". Furthermore, that new object will keep track of changes in the array (i.e. whenever you enumerate it, you'll see the current values of items).

Pavel Minaev
Creating an array to add a single value is a bit high on the overhead. It's a bit more re-usable to define an extension method for a single value Concat.
JaredPar
It depends - it may be worth it, but I'm tempted to call it premature optimization unless `Concat` is called in a loop repeatedly; and if it is, you have bigger problems anyway, because every time you `Concat`, you get one more layer of enumerator - so by the end of it the single call to `MoveNext` will result in a chain of calls equal in length to the number of iterations.
Pavel Minaev
@Pavel, heh. Usually it's not the memory overhead of creating the array that bothers me. It's the extra typing that I find annoying :).
JaredPar
So I understand a bit more what IEnumerable does. It should be view only not intend to be modifed. Thank you guys
ldsenow
I had a Connect feature request for syntactic sugar for `IEnumerable<T>` in C#, including overloading `operator+` as `Concat` (by the way, do you know that in VB, you can index `IEnumerable` as if it was an array - it uses `ElementAt` under the hood): https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=375929. If we also get two more overloads of `Concat` to take a single item both on the left and on the right, this would cut down the typing to `xs +=x ` - so go poke Mads (Torgersen) to prioritize this in C# 5.0 :)
Pavel Minaev
@Pavel, definitely know about the VB.Net feature (ran into it a few times tracking down bugs in the debugger). In terms of features it's probably faster to poke the BCL team to get them to add the extension methods (C# and BCL are separate teams).
JaredPar
+1  A: 

Others have already given great explanations regarding why you can not (and should not!) be able to add items to an IEnumerable. I will only add that if you are looking to continue coding to an interface that represents a collection and want an add method, you should code to ICollection or IList. As an added bonanza, these interfaces implement IEnumerable.

Jason
+1  A: 

Have you considered using ICollection<T> or IList<T> interfaces instead, they exist for the very reason that you want to have an 'Add' method on an IEnumerable<T>. IEnumerable<T> is used to 'mark' a type as being ...well.. enumerable or just a sequence of items without necessarily making any guarantees of whether the real underlying object supports adding/removing of items. Also remember that these interfaces implements IEnumerable<T> so you get all the extensions methods that you get with IEnumerable<T> as well.

Abhijeet Patel
+1 Good explanation.
Rex M