502

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>?

Vy Do
  • 46,709
  • 59
  • 215
  • 313
ldsenow
  • 5,885
  • 3
  • 21
  • 21
  • 6
    `IEnumerable` is meant for querying collections only. It is the backbone for the LINQ framework. It is always an abstraction of some other collection such as `Collection`, `List`, or `Array`. The interface only provides a `GetEnumerator` method that returns an instance of `IEnumerator` that enables the walking of the extending collection one item at a time. The LINQ extension methods are limited by this interface. `IEnumerable` is designed to be read only because it may represent an aggregation of parts of multiple collections. – Jordan Jan 27 '16 at 16:04
  • 3
    For this example, just declare `IList` instead of `IEnumerable`: `IList items = new T[]{new T("msg")}; items.Add(new T("msg2"));` – NightOwl888 May 07 '17 at 15:56

17 Answers17

570

You cannot, because IEnumerable<T> does not necessarily represent a collection to which 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 (!string.IsNullOrEmpty(s));
}

IEnumerable<string> lines = ReadLines();
lines.Add("foo") // so what is 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).

Jeankowkow
  • 814
  • 13
  • 33
Pavel Minaev
  • 99,783
  • 25
  • 219
  • 289
  • 3
    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 Jul 31 '09 at 02:08
  • 4
    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 Jul 31 '09 at 02:13
  • 2
    @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 Jul 31 '09 at 02:15
  • 2
    So I understand a bit more what IEnumerable does. It should be view only not intend to be modifed. Thank you guys – ldsenow Jul 31 '09 at 02:19
  • 7
    I had a Connect feature request for syntactic sugar for `IEnumerable` 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 Jul 31 '09 at 02:20
  • @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 Jul 31 '09 at 02:21
  • 2
    It's worth mentioning that Concat is, itself, an extension method. It won't be available unless you include System.Linq. – duggulous Nov 24 '14 at 21:16
124

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
  • 733,204
  • 149
  • 1,241
  • 1,454
  • 3
    @Pavel, thanks for pointing that out. Even Concat won't work though because it takes another IEnumerable. I pasted the typical extension method I define in my projects. – JaredPar Jul 31 '09 at 02:06
  • 18
    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 Jul 31 '09 at 02:15
  • @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 Jul 31 '09 at 02:18
  • 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 Jul 31 '09 at 02:24
  • 4
    I agree `Concat` overload would probably be a better choice as `Add` usually implies mutability. – Dan Abramov Jul 27 '11 at 16:50
  • 16
    What about modifying the body to `return e.Concat(Enumerable.Repeat(value,1));`? – Roy Tinker Oct 04 '11 at 01:06
  • 2
    The problem with naming it `Add` is that `List.Add` doesn't return a `List`, while mutating the original list, whereas your extension method `IEnumerable.Add` returns an `IEnumerable`, but doesn't mutate the original. That's enough of a behavior difference that I think `Add` is an inadvisable name. – ErikE Nov 12 '15 at 21:01
  • "The purpose of the IEnumerable interface is to allow a consumer to view the contents of a collection. Not to modify the values.", - a lightening. – Jeb50 May 11 '18 at 22:02
78

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 implement IEnumerable<T> so you get all the extensions methods that you get with IEnumerable<T> as well.

David Ferenczy Rogožan
  • 23,966
  • 9
  • 79
  • 68
Abhijeet Patel
  • 6,562
  • 8
  • 50
  • 93
73

In .net Core, there is a method Enumerable.Append that does exactly that.

The source code of the method is available on GitHub..... The implementation (more sophisticated than the suggestions in other answers) is worth a look :).

SourceOverflow
  • 1,960
  • 1
  • 8
  • 22
JanDotNet
  • 3,746
  • 21
  • 30
  • 6
    This is not only in core, also the framework 4.7.1 and newer: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.append?view=netframework-4.8 – sschoof May 21 '19 at 14:23
  • 3
    Note that Prepend also exists: https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.prepend?view=netframework-4.8&viewFallbackFrom=netframework-4.7 – aloisdg Jan 08 '20 at 09:56
  • Nice, exactly what I was looking for. Lots of answers miss this but you can simply return a new enumerable that first consumes the existing items, once there are no more, yield your extra item. I assume this is what Append does. – Yarek T Dec 08 '21 at 20:35
  • 5
    Just FYI - This method does not modify the elements of the collection. Instead, it creates a copy of the collection with the new element. - https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.append?view=net-6.0 – spacebread May 26 '22 at 20:04
  • @sschoof Append/Prepend are also available in .NET Standard 1.6+ – Rich Armstrong Mar 29 '23 at 19:39
37

A couple short, sweet extension methods on IEnumerable and IEnumerable<T> do it for me:

public static IEnumerable Append(this IEnumerable first, params object[] second)
{
    return first.OfType<object>().Concat(second);
}
public static IEnumerable<T> Append<T>(this IEnumerable<T> first, params T[] second)
{
    return first.Concat(second);
}   
public static IEnumerable Prepend(this IEnumerable first, params object[] second)
{
    return second.Concat(first.OfType<object>());
}
public static IEnumerable<T> Prepend<T>(this IEnumerable<T> first, params T[] second)
{
    return second.Concat(first);
}

Elegant (well, except for the non-generic versions). Too bad these methods are not in the BCL.

  • The first Prepend method is giving me an error. "'object[]' does not contain a definition for 'Concat' and the best extension method overload 'System.Linq.Enumerable.Concat(System.Collections.Generic.IEnumerable, System.Collections.Generic.IEnumerable)' has some invalid arguments" – Tim Newton Jul 23 '13 at 14:11
  • @TimNewton you're doing it wrong. Who knows why, as nobody can tell from that snippet of error code. Protip: always catch the exception and do a `ToString()` on it, as that captures more error details. I'd guess you didn't `include System.Linq;` at the top of your file, but who knows? Try figuring it out on your own, create a minimum prototype that exhibits the same error if you can't and then ask for help in a question. –  Jul 23 '13 at 14:45
  • I just copied and pasted the code above. All of them work ok except the Prepend which is giving the error I mentioned. Its not a runtime exception its a compile time exception. – Tim Newton Jul 23 '13 at 16:32
  • @TimNewton: I don't know what you're talking about it works perfectly you obviously are mistaken ignore the changes in the edit now I must be on my way, good day, sir. –  Jul 23 '13 at 16:40
  • maybe its something different about our environment. I'm using VS2012 and .NET 4.5. Dont waste anymore time on this I just thought I would point out that there is a problem with the above code, albeit a small one. I've upvoted this answer anyhow as its useful. thanks. – Tim Newton Jul 23 '13 at 16:49
  • @ErikE because not everything is generic, and it's trivial to implement. –  Nov 12 '15 at 21:32
29

No, the IEnumerable doesn't support adding items to it. The alternative solution is

var myList = new List(items);
myList.Add(otherItem);
Vy Do
  • 46,709
  • 59
  • 215
  • 313
Paul van Brenk
  • 7,450
  • 2
  • 33
  • 38
23

To add second message you need to -

IEnumerable<T> items = new T[]{new T("msg")};
items = items.Concat(new[] {new T("msg2")})
Aamol
  • 1,149
  • 1
  • 15
  • 23
15

I just come here to say that, aside from Enumerable.Concat extension method, there seems to be another method named Enumerable.Append in .NET Core 1.1.1. The latter allows you to concatenate a single item to an existing sequence. So Aamol's answer can also be written as

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

Still, please note that this function will not change the input sequence, it just return a wrapper that put the given sequence and the appended item together.

CXuesong
  • 552
  • 5
  • 12
10

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);
Sam Harwell
  • 97,721
  • 20
  • 209
  • 280
5

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
  • 236,483
  • 35
  • 423
  • 525
3

you can do this.

//Create IEnumerable    
IEnumerable<T> items = new T[]{new T("msg")};

//Convert to list.
List<T> list = items.ToList();

//Add new item to list.
list.add(new T("msg2"));

//Cast list to IEnumerable
items = (IEnumerable<T>)items;
pquest
  • 3,151
  • 3
  • 27
  • 40
Lrodriguez84
  • 714
  • 7
  • 8
2

Instances implementing IEnumerable and IEnumerator (returned from IEnumerable) don't have any APIs that allow altering collection, the interface give read-only APIs.

The 2 ways to actually alter the collection:

  1. If the instance happens to be some collection with write API (e.g. List) you can try casting to this type:

IList<string> list = enumerableInstance as IList<string>;

  1. Create a list from IEnumerable (e.g. via LINQ extension method toList():

var list = enumerableInstance.toList();

telepuz
  • 137
  • 1
  • 4
1

Easyest way to do that is simply

IEnumerable<T> items = new T[]{new T("msg")};
List<string> itemsList = new List<string>();
itemsList.AddRange(items.Select(y => y.ToString()));
itemsList.Add("msg2");

Then you can return list as IEnumerable also because it implements IEnumerable interface

1

IEnumerable items = Enumerable.Empty(T);

List somevalues = new List();

items.ToList().Add(someValues); items.ToList().AddRange(someValues);

MCreps
  • 11
  • 2
  • 1
    It is best to include commentary explaning your answer, especially seeing that there are many relevant answers, so as to explain why yours is different. Also consider formatting your code in code blocks. – Denis G. Labrecque Aug 16 '22 at 18:49
-2

Sorry for reviving really old question but as it is listed among first google search results I assume that some people keep landing here.

Among a lot of answers, some of them really valuable and well explained, I would like to add a different point of vue as, to me, the problem has not be well identified.

You are declaring a variable which stores data, you need it to be able to change by adding items to it ? So you shouldn't use declare it as IEnumerable.

As proposed by @NightOwl888

For this example, just declare IList instead of IEnumerable: IList items = new T[]{new T("msg")}; items.Add(new T("msg2"));

Trying to bypass the declared interface limitations only shows that you made the wrong choice. Beyond this, all methods that are proposed to implement things that already exists in other implementations should be deconsidered. Classes and interfaces that let you add items already exists. Why always recreate things that are already done elsewhere ?

This kind of consideration is a goal of abstracting variables capabilities within interfaces.

TL;DR : IMO these are cleanest ways to do what you need :

// 1st choice : Changing declaration
IList<T> variable = new T[] { };
variable.Add(new T());

// 2nd choice : Changing instantiation, letting the framework taking care of declaration
var variable = new List<T> { };
variable.Add(new T());

When you'll need to use variable as an IEnumerable, you'll be able to. When you'll need to use it as an array, you'll be able to call 'ToArray()', it really always should be that simple. No extension method needed, casts only when really needed, ability to use LinQ on your variable, etc ...

Stop doing weird and/or complex things because you only made a mistake when declaring/instantiating.

  • Downvoted since there are many real-world cases where you don't have control over the type of a variable that you are working with... e.g. an IEnumerable is a common return type for many data access library functions. This doesn't answer the question nor does it satisfactorily make the case that the question is rendered moot. – topsail Dec 04 '20 at 19:01
-2

Maybe I'm too late but I hope it helps anyone in the future.

You can use the insert function to add an item at a specific index. list.insert(0, item);

-7

Sure, you can (I am leaving your T-business aside):

public IEnumerable<string> tryAdd(IEnumerable<string> items)
{
    List<string> list = items.ToList();
    string obj = "";
    list.Add(obj);

    return list.Select(i => i);
}
  • I don't know why this answer has 8 downvotes, I find this solution working and not a bad idea. – Mayer Spitz Jun 08 '21 at 16:10
  • There is a better answer using the concat method instead of converting it to a list and then back to IEnumerable again. This solution works but is not performant. – Hulk Jun 16 '22 at 17:54