【问题标题】:When are collections enumerated (IEnumerable)何时枚举集合(IEnumerable)
【发布时间】:2014-01-26 22:02:34
【问题描述】:

最近,我遇到了一个奇怪的问题,我有一个方法可以生成 IEnumerable 对象集合。此方法包含返回四个对象的四个yield return 语句。我使用 var 关键字将结果分配给变量 results

var result = GenerateCollection().ToList();

这实际上意味着:List<MyType> result = GenerateCollection()

我对这个集合的元素做了一个简单的 for 循环。令我惊讶的是,每次调用列表(每个result[i])都会重新枚举该集合。后来我在一个 LINQ 查询中使用了result 集合,由于集合的不断重新枚举,它在性能方面产生了一些不好的结果。

我通过转换为数组而不是列表解决了这个问题。

这让我现在想知道的是什么时候枚举集合?哪些方法调用使集合重新枚举?

编辑:GenerateCollection() 方法看起来与此类似:

public static IEnumerable<MyType> GenerateCollection()
{
    var array = data.AsParallel(); //data is a simple collection of sublists of strings
    yield return new MyType("a", array.Where(x => x.Sublist.Count(y => y == 'a') == 0));
    yield return new MyType("b", array.Where(x => x.Sublist.Count(y => y == 'b') == 0));
    yield return new MyType("c", array.Where(x => x.Sublist.Count(y => y == 'c') == 0));
    yield return new MyType("d", array.Where(x => x.Sublist.Count(y => y == 'd') == 0));
}

【问题讨论】:

  • 请展示 GenerateCollection 方法的实现以及重现问题的每个循环
  • MyType 看起来如何?构造函数的第二个参数有什么作用?
  • 它基本上是一个字符串的元组和一个字符串列表。
  • 正如我在 SO 上可能已经说过一百次了:如果我只能告诉人们关于 LINQ 的一件事,那就是查询表达式产生一个 query 而不是 结果集。您正在提出一系列问题,而不是一系列答案。

标签: c# linq collections ienumerable yield-return


【解决方案1】:

您正在生成内部有查询的对象 - 它不是 array 值的某个序列 - 它的迭代器对象在您将它们传递给 MyType 的构造函数时不会执行。当您创建MyType 对象列表时

var result = GenerateCollection().ToList();

所有MyType 实例都被生成并保存到列表中,但如果您没有在MyType 构造函数中执行迭代器,则不会执行查询。甚至更多 - 如果您调用某个执行查询的运算符,它们将每次再次执行,例如

result[i].ArrayIterator.Count(); // first execution
foreach(var item in result[i].ArrayIterator) // second execution
    // ...

如果您将查询执行结果传递给MyType 构造函数,则可以修复它:

yield return new MyType("a", array.Where(x => !x.Sublist.Contains('a')).ToList())

现在您正在传递项目列表而不是迭代器(您可以使用ToArray())。在 yield MyType 实例时执行查询,不会再次执行。

【讨论】:

  • 我还会考虑将 ToList() 移动到 MyType 构造函数中是否更有意义。
【解决方案2】:
array.Where(x => x.Sublist.Count(y => y == 'a') == 0)

每次您在MyType 中访问它时,都会枚举这段代码。使用ToListToArray 确保仅在编写代码的地方枚举一次。

【讨论】:

    【解决方案3】:

    基于延迟执行的集合会在您使用它们时立即枚举,例如 IEnumerable、IQueryable 等,而基于立即执行的集合会在创建后立即枚举,例如 LIST。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-12-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-14
      • 2013-01-25
      • 2011-04-09
      相关资源
      最近更新 更多