如何将一个项目添加到IEnumerable<T>收集?

我的问题如上所述。例如

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

但毕竟里面只有一项。我们能有一个像items.Add(item)这样的方法吗?

761385 次浏览

不,IEnumerable不支持向其添加项。另一种解决方案是

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

IEnumerable<T>类型不支持这样的操作。IEnumerable<T>接口的目的是允许使用者查看集合的内容。不修改值。

当你执行像. tolist (). add()这样的操作时,你是在创建一个新的List<T>并将一个值添加到该列表中。它与原始列表没有关联。

你能做的就是使用Add扩展方法创建一个新的IEnumerable<T>

items = items.Add("msg2");

即使在这种情况下,它也不会修改原始的IEnumerable<T>对象。这可以通过持有对它的引用来验证。例如

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

在这组操作之后,变量temp仍然只引用值集中具有单个元素“foo”的枚举数,而项目将引用值为“foo”和“bar”的不同枚举数。

编辑

我经常忘记Add不是IEnumerable<T>上的典型扩展方法,因为它是我最终定义的第一个扩展方法之一。在这里

public static IEnumerable<T> Add<T>(this IEnumerable<T> e, T value) {
foreach ( var cur in e) {
yield return cur;
}
yield return value;
}

不仅不能像state那样添加项,而且如果将项添加到已有枚举器的List<T>(或几乎任何其他非只读集合)中,枚举器将失效(从那时开始抛出InvalidOperationException)。

如果你正在聚合来自某种类型的数据查询的结果,你可以使用Concat扩展方法:

编辑:我最初在示例中使用Union扩展名,这是不正确的。我的应用程序广泛地使用它来确保重叠的查询不会重复结果。

IEnumerable<T> itemsA = ...;
IEnumerable<T> itemsB = ...;
IEnumerable<T> itemsC = ...;
return itemsA.Concat(itemsB).Concat(itemsC);

不能,因为IEnumerable<T>并不一定代表一个可以添加项的集合。事实上,它并不一定代表一个集合!例如:

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

然而,你能做的是创建一个 IEnumerable对象(未指定类型),当枚举时,它将提供旧对象的所有项,加上你自己的一些项。你可以使用Enumerable.Concat来实现:

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

这个不会改变数组对象(无论如何,你不能将项插入到数组中)。但它将创建一个新对象,该对象将列出数组中的所有项,然后是“Foo”。此外,这个新对象将跟踪数组中的变化(即每当你枚举它时,你将看到项目的当前值)。

其他人已经给出了关于为什么你不能(也不应该!)能够向IEnumerable添加项的很好的解释。我只会补充说,如果你想继续编码一个表示集合的接口,并且想要一个add方法,你应该编码到ICollectionIList。作为额外的财富,这些接口实现了IEnumerable

你是否考虑过使用ICollection<T>IList<T>接口来代替,它们存在的原因正是你想在IEnumerable<T>上有一个Add方法。

IEnumerable<T>用于将一个类型“标记”为…嗯,可枚举的,或者只是一个项的序列,而不必保证真正的底层对象是否支持添加/删除项。还要记住,这些接口实现了IEnumerable<T>,所以你也可以得到所有通过IEnumerable<T>得到的扩展方法。

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

优雅(好吧,非通用版本除外)。可惜这些方法不在BCL中。

你可以做到的。

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

最简单的方法很简单

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

然后你可以返回list作为IEnumerable,因为它实现了IEnumerable接口

要添加第二条消息,您需要-

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

当然,你可以(我把你的T-business放在一边):

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


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

我只是想说,除了Enumerable.Concat扩展方法之外,在. net Core 1.1.1中似乎还有另一个名为Enumerable.Append的方法。后者允许您将单个项连接到现有序列。所以Aamol的答案也可以写成

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

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

在.net Core中,有一个方法Enumerable.Append就是做这个的。

该方法的源代码是可以在GitHub上找到。....实现(比其他答案中的建议更复杂)值得一看:)。

很抱歉,我又提了一个老问题,但由于它出现在谷歌搜索结果的第一个列表中,我想有些人会一直登陆这里。

在许多答案中,其中一些非常有价值,并且解释得很好,我想补充一个不同的观点,因为对我来说,问题还没有很好地确定。

你正在声明一个存储数据的变量,你需要它能够通过添加项目来改变吗?所以你不应该把它声明为IEnumerable。

由@NightOwl888提议

对于本例,只需声明IList而不是IEnumerable: IList items = new T[]{new T("msg")};物品。添加(新T(“msg2"));

试图绕过声明的接口限制只表明您做出了错误的选择。 除此之外,所有提议用来实现其他实现中已经存在的东西的方法都应该考虑。 允许您添加项的类和接口已经存在。

?

这种考虑是在接口中抽象变量功能的目标。

TL;DR:在我看来,这些是做你需要的事情的最干净的方法:

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

当您需要使用变量作为IEnumerable时,您将能够。当你需要将它作为数组使用时,你可以调用'ToArray()',它总是应该这么简单。不需要扩展方法,仅在真正需要时才强制转换,能够在变量上使用LinQ,等等…

停止做奇怪和/或复杂的事情,因为你只是在声明/实例化时犯了一个错误。

实现IEnumerable和IEnumerator(从IEnumerable返回)的实例没有任何允许修改收集的api,接口提供只读api。

实际上改变集合的2种方法:

  1. 如果实例恰好是一些写API的集合(例如List),你可以尝试转换为这种类型:

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

  1. 从IEnumerable中创建一个列表(例如通过LINQ扩展方法toList():

var list = enumerableInstance.toList();

也许我说得太迟了,但我希望这对将来的任何人都有帮助。

您可以使用insert函数在特定索引处添加一个项。 list.insert(0, item); < / p >

IEnumerable items = Enumerable.Empty(T);

List somevalues = new List();

< p > items.ToList阀门()(someValues); items.ToList () .AddRange (someValues); < / p >