最近,我遇到了一个奇怪的问题,我有一个方法生成一个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));
}
答案 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
中访问时,都会枚举这段代码。使用ToList
或ToArray
确保只在代码编写的位置枚举一次。
答案 2 :(得分:1)
基于延迟执行的集合会在您使用它们时立即枚举。例如IEnumerable,IQueryable等,基于立即执行的集合会在创建它们时立即枚举。例如LIST。