How can I add an item to a IEnumerable<T> collection?
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>
?
评论
回答1
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).
回答2
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;
}
回答3
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.
回答4
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 :).
IEnumerable<T>
is meant for querying collections only. It is the backbone for the LINQ framework. It is always an abstraction of some other collection such asCollection<T>
,List<T>
, orArray
. The interface only provides aGetEnumerator
method that returns an instance ofIEnumerator<T>
that enables the walking of the extending collection one item at a time. The LINQ extension methods are limited by this interface.IEnumerable<T>
is designed to be read only because it may represent an aggregation of parts of multiple collections.