何时枚举集合(IEnumerable)

时间:2014-01-26 22:02:35

标签: c# linq collections ienumerable yield-return

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

var result = GenerateCollection().ToList();

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

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

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

这让我想知道现在的枚举是什么时候?哪个方法调用make collection重新枚举?

编辑: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));
}

3 个答案:

答案 0 :(得分:2)

您正在产生具有查询内部的对象 - 它不是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())。当你产生MyType实例时执行查询,它将不再被执行。

答案 1 :(得分:1)

array.Where(x => x.Sublist.Count(y => y == 'a') == 0)

每次在MyType中访问时,都会枚举这段代码。使用ToListToArray确保只在代码编写的位置枚举一次。

答案 2 :(得分:1)

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