代码之家  ›  专栏  ›  技术社区  ›  Toshi

执行IEnumberable[副本]

  •  1
  • Toshi  · 技术社区  · 6 年前

    这个问题已经有了答案:

    我有这个密码

    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 回复  |  直到 5 年前
        1
  •  3
  •   bommelding    6 年前

    对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()将是代价高昂的反优化。

        2
  •  1
  •   g.pickardou    6 年前

    澄清:

    在你的队伍里:

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

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

    当您开始迭代 even ,例如 foreach GetEnumerator() 然后 Next() FirstOrDefault 或ToList()或ToArray()等。

    尽管我们没有看到你 DoStuffX() 代码可能是你做了类似的事情。

    回答

    您所要求的ar主要独立于上面描述的延迟评估:它是关于缓存的。如果不缓存结果,则会多次发生迭代。计算(迭代)和缓存的最简单模式是执行ToList()

    var cached = even.ToList();
    

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

        3
  •  0
  •   TomTom    6 年前

    他怎么能记住它?不可能。必须执行。

    延迟并不意味着缓存。意思是这条线

    IEnumerable even=项。其中(x=>x%2==0);

    不能执行任何操作。别开玩笑了-可能会延期。

    然后排队

    剂量1(偶数);

    它可能会执行。

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