我有这个代码
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()
)还是以某种方式保存在内存中?
答案 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
上开始迭代时(例如,先用foreach
或GetEnumerator()
然后用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并在请求第一项时执行它。