【问题标题】:C# IEnumerable being reset in child methodC# IEnumerable 在子方法中被重置
【发布时间】:2017-09-21 10:36:46
【问题描述】:

我有以下方法:

private static List<List<job>> SplitJobsByMonth(IEnumerable<job> inactiveJobs)
{
    List<List<job>> jobsByMonth = new List<List<job>>();

    DateTime cutOff = DateTime.Now.Date.AddMonths(-1).Date;
    cutOff = cutOff.AddDays(-cutOff.Day + 1);

    List<job> temp;
    while (inactiveJobs.Count() > 0)
    {
        temp = inactiveJobs.Where(j => j.completeddt >= cutOff).ToList();
        jobsByMonth.Add(temp);
        inactiveJobs = inactiveJobs.Where(a => !temp.Contains(a));
        cutOff = cutOff.AddMonths(-1);
    }

    return jobsByMonth;
}

它旨在按月拆分工作。 'job' 是一个类,而不是一个结构。在 while 循环中,传入的 IEnumerable 在每次迭代时都会重置,以移除已处理的作业:

inactiveJobs = inactiveJobs.Where(a => !temp.Contains(a));

通常这会大大减少此集合的内容。但是,在下一次迭代中:

temp = inactiveJobs.Where(j => j.completeddt >= cutOff).ToList();

将 inactiveJobs 对象恢复到它被传递到方法时的状态 - 所以集合再次满了。 我已经通过稍微重构这个方法解决了这个问题,但是我很好奇为什么会出现这个问题,因为我无法解释它。谁能解释为什么会这样?

【问题讨论】:

  • inactiveJobs = inactiveJobs.Where(a =&gt; !temp.Contains(a)); 行添加 ToList 或 ToArray 解决了问题?
  • 我认为这是由 linq 的延迟执行引起的。因此,当使用Where 时,您实际上并没有执行 查询,而只是定义 它。只需像之前的行一样使用Where(...).ToList()
  • stackoverflow.com/help/mcve - 如果您可以向我们展示具有预期输出的完整示例,我们可以帮助您转向该解决方案。
  • 添加 ToList() 修复了它。谢谢。

标签: c# linq ienumerable


【解决方案1】:

为什么不直接使用 group by?

    private static List<List<job>> SplitJobsByMonth(IEnumerable<job> inactiveJobs)
    {
        var jobsByMonth = (from job in inactiveJobs
                            group job by new DateTime(job.completeddt.Year, job.completeddt.Month, 1)
                            into g
                            select g.ToList()).ToList();
        return jobsByMonth;
    }

【讨论】:

    【解决方案2】:

    这是因为延迟执行 LINQ 的 Where

    当你这样做时

    inactiveJobs = inactiveJobs.Where(a => !temp.Contains(a));
    

    在您开始迭代 IEnumerable 之前,实际上不会进行任何评估。如果在Where 后面加上ToList,就会马上迭代,所以interactiveJobs 的内容会减少:

    inactiveJobs = inactiveJobs.Where(a => !temp.Contains(a)).ToList();
    

    【讨论】:

      【解决方案3】:

      LINQ 中,查询有两种不同的执行行为:立即延迟

      查询实际上是在迭代查询变量时执行的,而不是在创建查询变量时。这称为延迟执行。

      您还可以强制查询立即执行,这对于缓存查询结果很有用。

      为了在你的行尾添加.ToList()

      inactiveJobs = inactiveJobs.Where(a => !temp.Contains(a)).ToList();
      

      这会立即执行创建的查询并将结果写入您的变量。

      你可以看到更多关于this example Here的信息。

      【讨论】:

        猜你喜欢
        • 2012-07-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-10-05
        • 2018-01-27
        • 2010-12-16
        • 2018-04-15
        相关资源
        最近更新 更多