简体   繁体   English

如何将项目添加到 IEnumerable<t> 收藏?</t>

[英]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> ?我们可以有像List<T>这样的items.Add(item)的方法吗?

You cannot, because IEnumerable<T> does not necessarily represent a collection to which items can be added.您不能,因为IEnumerable<T>不一定代表可以添加项目的集合。 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.但是,您可以做的是创建一个IEnumerable对象(未指定类型),枚举该对象时,将提供旧对象的所有项目,以及您自己的一些项目。 You use Enumerable.Concat for that:您为此使用Enumerable.Concat

 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".但它将创建一个新对象,该对象将列出数组中的所有项目,然后是“Foo”。 Furthermore, that new object will keep track of changes in the array (ie whenever you enumerate it, you'll see the current values of items).此外,该新对象将跟踪数组中的更改(即,无论何时枚举它,您都会看到项目的当前值)。

The type IEnumerable<T> does not support such operations. IEnumerable<T>类型不支持此类操作。 The purpose of the IEnumerable<T> interface is to allow a consumer to view the contents of a collection. IEnumerable<T>接口的目的是允许消费者查看集合的内容。 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.当您执行 .ToList().Add() 之类的操作时,您正在创建一个新的List<T>并向该列表添加一个值。 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.您可以做的是使用 Add 扩展方法创建一个具有附加值的新IEnumerable<T>

items = items.Add("msg2");

Even in this case it won't modify the original IEnumerable<T> object.即使在这种情况下,它也不会修改原始的IEnumerable<T>对象。 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".在这组操作之后,变量 temp 仍将仅引用值集中具有单个元素“foo”的可枚举,而项目将引用具有值“foo”和“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.我经常忘记 Add 不是IEnumerable<T>上的典型扩展方法,因为它是我最终定义的第一个方法。 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;
}

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> .您是否考虑过使用ICollection<T>IList<T>接口,它们的存在是因为您希望在IEnumerable<T>上有一个Add方法。

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. IEnumerable<T>用于将类型“标记”为......好吧,可枚举或只是一系列项目,而不必保证真正的底层对象是否支持添加/删除项目。 Also remember that these interfaces implement IEnumerable<T> so you get all the extensions methods that you get with IEnumerable<T> as well.还要记住,这些接口实现了IEnumerable<T> ,因此您也可以获得使用IEnumerable<T>获得的所有扩展方法。

In .net Core, there is a method Enumerable.Append that does exactly that.在 .net Core 中,有一个Enumerable.Append方法可以做到这一点。

The source code of the method is available on GitHub.该方法的源代码可在 GitHub 上找到。 .... The implementation (more sophisticated than the suggestions in other answers) is worth a look :). ....实现(比其他答案中的建议更复杂)值得一看:)。

A couple short, sweet extension methods on IEnumerable and IEnumerable<T> do it for me: IEnumerableIEnumerable<T>上的几个简短而甜蜜的扩展方法为我做:

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.太糟糕了,这些方法不在 BCL 中。

No, the IEnumerable doesn't support adding items to it.不, IEnumerable不支持向其中添加项目。 The alternative solution is替代解决方案是

var myList = new List(items);
myList.Add(otherItem);

To add second message you need to -要添加第二条消息,您需要 -

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

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.我只是来这里说一下,除了Enumerable.Concat扩展方法外,.NET Core 1.1.1 中似乎还有另一种名为Enumerable.Append的方法。 The latter allows you to concatenate a single item to an existing sequence.后者允许您将单个项目连接到现有序列。 So Aamol's answer can also be written as所以 Aamol 的答案也可以写成

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.不过,请注意这个函数不会改变输入序列,它只是返回一个将给定序列和附加项放在一起的包装器。

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).您不仅不能像您所说的那样添加项目,而且如果您将项目添加到具有现有枚举器的List<T> (或几乎任何其他非只读集合),则枚举器无效(抛出InvalidOperationException从那时起)。

If you are aggregating results from some type of data query, you can use the Concat extension method:如果您正在聚合来自某种类型的数据查询的结果,则可以使用Concat扩展方法:

Edit: I originally used the Union extension in the example, which is not really correct.编辑:我最初在示例中使用了Union扩展,这并不正确。 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);

Others have already given great explanations regarding why you can not (and should not!) be able to add items to an IEnumerable .其他人已经对为什么您不能(也不应该!)能够将项目添加到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 .我只会补充一点,如果您希望继续对表示集合的接口进行编码并想要一个 add 方法,则应该对ICollectionIList进行编码。 As an added bonanza, these interfaces implement IEnumerable .作为额外的财富,这些接口实现了IEnumerable

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;

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然后您也可以将列表作为 IEnumerable 返回,因为它实现了 IEnumerable 接口

Instances implementing IEnumerable and IEnumerator (returned from IEnumerable) don't have any APIs that allow altering collection, the interface give read-only APIs.实现 IEnumerable 和 IEnumerator(从 IEnumerable 返回)的实例没有任何允许更改集合的 API,该接口提供只读 API。

The 2 ways to actually alter the collection:实际更改集合的两种方法:

  1. If the instance happens to be some collection with write API (eg List) you can try casting to this type:如果该实例恰好是带有写入 API 的某个集合(例如 List),您可以尝试转换为这种类型:

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

  1. Create a list from IEnumerable (eg via LINQ extension method toList() :从 IEnumerable 创建一个列表(例如通过 LINQ 扩展方法toList()

var list = enumerableInstance.toList();

IEnumerable items = Enumerable.Empty(T); IEnumerable items = Enumerable.Empty(T);

List somevalues = new List();列出一些值 = new List();

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

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.在很多答案中,其中一些非常有价值且解释得很好,我想补充一点 vue 的不同之处,因为对我来说,问题还没有得到很好的识别。

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.所以你不应该使用将它声明为 IEnumerable。

As proposed by @NightOwl888正如@NightOwl888 所提议的那样

For this example, just declare IList instead of IEnumerable: IList items = new T[]{new T("msg")};对于此示例,只需声明 IList 而不是 IEnumerable: IList items = new T[]{new T("msg")}; items.Add(new T("msg2")); 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 : TL; DR:IMO 这些是满足您需要的最干净的方法:

// 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.当您需要将变量用作 IEnumerable 时,您将能够做到。 When you'll need to use it as an array, you'll be able to call 'ToArray()', it really always should be that simple.当您需要将它用作数组时,您将能够调用“ToArray()”,它真的应该总是那么简单。 No extension method needed, casts only when really needed, ability to use LinQ on your variable, etc ...无需扩展方法,仅在真正需要时进行转换,能够在变量上使用 LinQ,等等......

Stop doing weird and/or complex things because you only made a mistake when declaring/instantiating.停止做奇怪和/或复杂的事情,因为你只是在声明/实例化时犯了一个错误。

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);

Sure, you can (I am leaving your T-business aside):当然,您可以(我将您的 T 业务放在一边):

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

    return list.Select(i => i);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM