关于c#:如何将项添加到IEnumerable

How can I add an item to a IEnumerable<T> collection?

我的问题作为以上标题。例如,

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

但毕竟里面只有一件东西。

我们能有一个像items.Add(item)这样的方法吗?

List一样


您不能,因为IEnumerable不一定表示可以向其中添加项的集合。实际上,它根本不一定代表一个集合!例如:

1
2
3
4
5
6
7
8
9
10
11
12
IEnumerable<string> ReadLines()
{
     string s;
     do
     {
          s = Console.ReadLine();
          yield return s;
     } while (s !="");
}

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

但是,您可以创建一个新的IEnumerable对象(未指定类型),在枚举时,它将提供旧对象的所有项以及您自己的一些项。您可以使用Enumerable.Concat进行以下操作:

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

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


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

当您执行像.to list().add()这样的操作时,您将创建一个新的List,并向该列表中添加一个值。它与原始列表没有连接。

您所能做的就是使用添加扩展方法创建一个具有附加值的新IEnumerable

1
items = items.Add("msg2");

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

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

在这组操作之后,变量temp仍将只引用值集中单个元素"foo"的可枚举值,而项将引用值为"foo"和"bar"的不同可枚举值。

编辑

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

1
2
3
4
5
6
public static IEnumerable<T> Add<T>(this IEnumerable<T> e, T value) {
  foreach ( var cur in e) {
    yield return cur;
  }
  yield return value;
}


您是否考虑改用ICollection或IList接口,它们的存在正是因为您希望在IEnumerable上使用"添加"方法。IEnumerable用于将一个类型"标记"为……嗯..可枚举的或只是一系列项,而不必保证真正的基础对象是否支持添加/删除项。还请记住,这些接口实现IEnumerable,因此您也可以使用IEnumerable获得所有扩展方法。


IEnumerableIEnumerable上有两种简短的、甜蜜的扩展方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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中。


不,IEnumerable不支持向其中添加项。

您的"备选方案"是:

1
2
var List = new List(items);
List.Add(otherItem);


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

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


在.NET核心中,有一个方法Enumerable.Append可以做到这一点。

方法的源代码在Github上可用…..实现(比其他答案中的建议更复杂)值得一看。


不仅不能添加您声明的项目,而且如果您向现有枚举器的List中添加了一个项目(或几乎任何其他非只读集合),枚举器将失效(从那时起抛出InvalidOperationException)。

如果要聚合某些类型数据查询的结果,可以使用Concat扩展方法:

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

1
2
3
4
IEnumerable<T> itemsA = ...;
IEnumerable<T> itemsB = ...;
IEnumerable<T> itemsC = ...;
return itemsA.Concat(itemsB).Concat(itemsC);

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

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

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


其他人已经对你为什么不能(也不应该)给出了很好的解释。能够在IEnumerable中添加项目。我只想补充一点,如果您希望继续对表示集合的接口进行编码,并且希望使用add方法,那么您应该对ICollectionIList进行编码。作为一个额外的财富,这些接口实现了IEnumerable


你可以这样做。

1
2
3
4
5
6
7
8
9
10
11
//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;


最简单的方法就是

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

然后您还可以将列表作为IEnumerable返回,因为它实现了IEnumerable接口


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

1
2
3
4
5
6
7
8
public IEnumerable<string> tryAdd(IEnumerable<string> items)
{
    List<string> list = items.ToList();
    string obj ="";
    list.Add(obj);

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