IEnumberable

时间:2018-07-26 08:56:23

标签: c# linq ienumerable enumerable

我有这个代码

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())还是以某种方式保存在内存中?

3 个答案:

答案 0 :(得分:3)

  

对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))这样的主循环时,则main方法中的ToList()将是昂贵的去优化。

答案 1 :(得分:1)

说明:

您所在的行:

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

不对表达式求值,因此实际上不会发生迭代。这就是所谓的“递延评估”。注意:这从概念上讲是独立于接口实现的,因此无论它是List <>还是[]等等(当然可以针对这个主要概念进行实现,但这不在问题范围内)

当您在even上开始迭代时(例如,先用foreachGetEnumerator()然后用Next()FirstOrDefault或ToList()或ToArray来开始执行) ()等。

尽管我们看不到您的DoStuffX()代码,但您可能执行了类似的操作。

答案

您询问的内容主要独立于上述延迟评估:与缓存有关。如果不缓存结果,则迭代将发生多次。评估(迭代)和缓存的最简单模式是执行ToList()

var cached = even.ToList();

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

答案 2 :(得分:0)

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

推迟并不意味着缓存。这意味着该行

  

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

可能不执行任何操作。别开玩笑-这可能会推迟。

然后在行

DoStuff1(even);

它可以执行。

当项目不是内存列表而是数据库(可能是cmplex查询)时,人们会说:“哦,我的foreach在到达第一行之前花了1分钟”-这就是延误的意思。在这种情况下,它将仅发送SQL并在请求第一项时执行它。