Performance of IEnumerable



我有这个代码

List<int> items = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
//calculation is not being executed until "even" is used
IEnumerable<int> even = items.Where(x => x % 2 == 0);   
DoStuff1(even);
DoStuff2(even);
DoStuff3(even);

我读过这个答案https://stackoverflow.com/a/3628705/6887468上面写着

当您使用IEnumerable时,您给编译器一个推迟工作的机会,可能会在这一过程中进行优化。如果使用ToList((,则会强制编译器立即具体化结果。

现在这个计算(在我的例子中是x % 2 == 0(是为DoStuff()的每个调用执行的,还是以某种方式保存在内存中?

为DoStuff((的每次调用执行

,或者这在内存中是否存在?

它本身不在内存中,因此以下内容有时是值得优化的:

IEnumerable<int> even = items.Where(x => x % 2 == 0);   
even = even.ToList();  // now it is held in memory
DoStuff1(even);
DoStuff2(even);
DoStuff3(even);

是否需要ToList((取决于DoStuff((的作用、源列表与过滤列表的大小、枚举的成本等。

请注意,当items稍长,并且所有DoStuff方法都有一个类似foreach(var item in items.Take(3))的主循环时,那么主方法中的ToList((将是一个昂贵的去优化。

澄清:

在你的行:

IEnumerable<int> even = items.Where(x => x % 2 == 0); 

表达式没有求值,因此迭代实际上不会发生。这就是所谓的"延期评估"。注意:这在概念上独立于接口实现,因此无论它是List<>或者[]等等(当然可以针对这个主要概念进行实现,但这超出了问题的范围(

当您开始对even进行迭代时,就会开始执行,例如使用foreachGetEnumerator(),然后使用Next()FirstOrDefault或ToList((或ToArray((等。

虽然我们没有看到您的DoStuffX()代码,但您可能也做了类似的事情。

回答

您所问的问题主要与上述延迟评估无关:它是关于缓存的。如果不缓存结果,迭代会发生多次。评估(迭代(和缓存的最简单模式是执行ToList((

var cached = even.ToList();

(请注意,并非所有IEnumerable实现都允许多次迭代,但List<>允许。

他如何将其保存在内存中?不可能。必须执行。

延迟并不意味着缓存。这意味着线路

IEnumerable偶数=项。其中(x=>x%2==0(;

可能不会执行任何操作。不开玩笑,它可能会推迟。

然后在行

DoStuff1(偶数(;

它可以执行。

当项目不是内存中的列表,而是数据库,可能是cmplex的查询时,人们会说"哦,我的foreach在得到第一行之前花了1分钟"——这就是deffered的意思。在这种情况下,它只发送SQL,并在请求第一项时执行它。

最新更新