如何将项添加到IEnumerable <T>集合中?

lds*_*now 382 c# ienumerable list

我的问题如上所述.例如,

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

但毕竟它里面只有1个项目.

我们可以有一个方法items.Add(item)吗?

List<T>

Pav*_*aev 455

您不能,因为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??
Run Code Online (Sandbox Code Playgroud)

但是,你可以做的是创建一个 IEnumerable对象(未指定类型),当枚举时,它将提供旧对象的所有项目,以及你自己的一些项目.你用Enumerable.Concat它:

 items = items.Concat(new[] { "foo" });
Run Code Online (Sandbox Code Playgroud)

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

  • 我在C#中对`IEnumerable <T>`有一个连接功能请求语法糖,包括将`operator +`重载为`Concat`(顺便说一句,你知道在VB中你可以索引`IEnumerable`就像它是一个数组 - 它在引擎盖下使用`ElementAt`:https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedidID = 375929.如果我们还得到另外两个`Concat`的重载来在左侧和右侧同时采用一个项目,这会将键入内容减少为"xs + = x" - 所以请捅Mads(Torgersen)来优先考虑C#5.0 :) (7认同)
  • 这取决于 - 它可能是值得的,但我很想称之为过早优化,除非在循环中重复调用`Concat`; 如果是的话,你还有更大的问题,因为每次你`Concat',你会得到一层枚举器 - 所以在它结束时,对`MoveNext`的单次调用将导致一个长度相等的调用链到迭代次数. (4认同)
  • 创建一个数组以添加单个值在开销上有点高.为单个值Concat定义扩展方法更加可重用. (3认同)
  • @Pavel,嘿.通常这不是创建困扰我的数组的内存开销.这是我觉得烦人的额外打字:). (2认同)
  • 所以我更了解IEnumerable的作用.应该只是不打算修改视图.感谢你们 (2认同)
  • 值得一提的是,Concat本身就是一种扩展方法。除非包含System.Linq,否则它将不可用。 (2认同)

Jar*_*Par 89

该类型IEnumerable<T>不支持此类操作.IEnumerable<T>界面的目的是允许使用者查看集合的内容.不要修改值.

当您执行.ToList().Add()等操作时,您将创建一个新的List<T>并向该列表添加值.它与原始列表没有任何关联.

您可以使用Add扩展方法创建IEnumerable<T>具有附加值的new .

items = items.Add("msg2");
Run Code Online (Sandbox Code Playgroud)

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

var items = new string[]{"foo"};
var temp = items;
items = items.Add("bar");
Run Code Online (Sandbox Code Playgroud)

在这组操作之后,变量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;
}
Run Code Online (Sandbox Code Playgroud)

  • 如何将body修改为`return e.Concat(Enumerable.Repeat(value,1));`? (15认同)
  • 这就是所谓的"Concat":) (12认同)
  • 我不会称之为"添加",因为实际上任何其他.NET类型(不仅仅是集合)上的"添加"都可以就地修改集合.也许`With`?或者它甚至可能只是`Concat`的另一个重载. (10认同)
  • 我同意`Concat`重载可能是一个更好的选择,因为`Add`通常意味着可变性. (4认同)
  • @Pavel,谢谢你指出这一点.即使Concat也无法工作,因为它需要另一个IEnumerable <T>.我粘贴了我在项目中定义的典型扩展方法. (3认同)
  • 将其命名为“Add”的问题是“List.Add”在改变原始列表时不会返回“List”,而扩展方法“IEnumerable.Add”返回“IEnumerable”,但不会改变原本的。行为差异已经足够了,我认为“Add”是一个不明智的名称。 (2认同)

Abh*_*tel 53

您是否考虑使用ICollection <T>IList <T>接口,它们的存在是因为您希望在IEnumerable <T>上使用"添加"方法.IEnumerable <T>用于将类型"标记"为......井......可枚举或只是一系列项目,而不必保证真实底层对象是否支持添加/删除项目.还要记住,这些接口实现了IEnumerable <T>,因此您获得了使用IEnumerable <T>获得的所有扩展方法.


小智 31

一些简短,甜蜜的扩展方法,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);
}
Run Code Online (Sandbox Code Playgroud)

优雅(嗯,非通用版本除外).太糟糕了,这些方法都不在BCL中.


Pau*_*enk 19

没有IEnumerable不支持向其添加项目.

你'另类'是:

var List = new List(items);
List.Add(otherItem);
Run Code Online (Sandbox Code Playgroud)

  • 当然你也无法实例化 - 它们是接口. (8认同)
  • 但你也无法实例化. (5认同)
  • 你的建议不是唯一的选择.例如,ICollection和IList都是合理的选择. (4认同)

Jan*_*Net 18

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

该方法的源代码可在GitHub上获得.....实施(比其他答案中的建议更复杂)值得一看:).

  • 仅供参考 - 此方法不会修改集合的元素。相反,它使用新元素创建集合的副本。- https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.append?view=net-6.0 (6认同)
  • 请注意,前置也存在:https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.prepend?view=netframework-4.8&amp;viewFallbackFrom=netframework-4.7 (3认同)
  • 这不仅在核心,而且在框架4.7.1及更高版本中:https://docs.microsoft.com/zh-cn/dotnet/api/system.linq.enumerable.append?view=netframework-4.8 (2认同)

Aam*_*mol 14

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

IEnumerable<T> items = new T[]{new T("msg")};
items = items.Concat(new[] {new T("msg2")})
Run Code Online (Sandbox Code Playgroud)


Sam*_*ell 8

您不仅可以添加像您所述的项目,而且如果您将项目添加到List<T>(或几乎任何其他非读取集合)您拥有现有枚举器,则枚举器将失效(InvalidOperationException从此开始抛出).

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

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

IEnumerable<T> itemsA = ...;
IEnumerable<T> itemsB = ...;
IEnumerable<T> itemsC = ...;
return itemsA.Concat(itemsB).Concat(itemsC);
Run Code Online (Sandbox Code Playgroud)


CXu*_*ong 6

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

IEnumerable<T> items = new T[]{new T("msg")};
items = items.Append(new T("msg2"));
Run Code Online (Sandbox Code Playgroud)

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


jas*_*son 5

其他人已经给出了关于为什么您不能(也不应该!)能够将项目添加到IEnumerable. 我只会补充一点,如果您希望继续对表示集合的接口进行编码并想要一个 add 方法,那么您应该对ICollectionor进行编码IList。作为额外的财富,这些接口实现了IEnumerable.