【问题标题】:How to rewrite several independent LINQ queries into single one using Aggregate()?如何使用 Aggregate() 将多个独立的 LINQ 查询重写为单个查询?
【发布时间】:2025-11-29 20:55:02
【问题描述】:

我有下一个非常未优化的代码:

void Summarize(IEnumerable<Section> sections)
{
    this.DateBegin = sections.Select(s => s.Date).Min();
    this.DateEnd = sections.Select(s => s.Date).Max();
    this.Income = sections.Where(s => s.IsIncome).Sum(r => r.Amount);
    this.ExpenditureBank = sections.Where(s => s.IsExpenditureBank).Sum(r => r.Amount);
    this.ExpenditureClient = sections.Where(s => s.IsExpenditureClient).Sum(r => r.Amount);
    this.Expenditure = this.ExpenditureBank + this.ExpenditureClient;
}

如果适用,如何使用IEnumerable.Aggregate() 重写它?

【问题讨论】:

    标签: c# .net linq ienumerable aggregate


    【解决方案1】:

    有时,老式的foreach 循环是完成这项工作的最佳工具。我认为这里就是这种情况,否则你要么会多次枚举这些部分,要么会有一些非常不可读的 LINQ 代码。

    尽管您可能提前知道sections 参数将是一个数组或集合或其他什么,但它可能是枚举起来非常昂贵的东西,或者枚举之间不会产生一致值的东西。 IEnumerable 有时会让你大吃一惊。

    【讨论】:

      【解决方案2】:

      我的解决方案与 Codesleuth 的解决方案非常接近,但会首先定义一个 Summary Struct

      struct Summary 
      {
          public DateTime DateBegin {get; set;}
          public DateTime DateEnd {get; set;}
          public int Income  {get; set;}
          public int ExpenditureBank {get; set;}
          public int ExpenditureClient {get; set;}
          public int Expenditure {get {return ExpenditureBank+ExpenditureClient; }}
      }
      
      void Summarize(IEnumerable<Section> sections)
      {
          var result = sections.Aggregate(new Summary
          {
              DateBegin = DateTime.MaxValue,
              DateEnd = DateTime.MinValue,
              Income = 0,
              ExpenditureBank =0,
              ExpenditureClient =0,
          },
          (agg, next) =>
          new Summary
          {
              DateBegin = next.Date < agg.DateBegin ? next.Date : agg.DateBegin,
              DateEnd = next.Date > agg.DateEnd ? next.Date : agg.DateEnd,
              Income = agg.Income + (next.IsIncome ? next.Amount : 0),
              ExpenditureBank = next.IsExpenditureBank ? next.Amount: 0,
              ExpenditureClient = next.IsExpenditureClient ? next.Amount : 0
          });
      }
      

      请注意,理想情况下结构是不可变的。

      【讨论】:

      • 和我的回答基本一样。但是,当ExpenditureBankExpenditureClient 为真时,您还没有合并金额的结果——这个问题不可避免地会发生。为此,您的答案可以简化。
      • 我能否指出我们的两个答案都非常低效。在sections 参数中为Sections 的每个实例创建一个新对象完全是浪费。这个问题非常不切实际。但是,我们的答案正确的。
      • 这取决于创建一个新对象,我们可以利用聚合是关联的这一事实,因此我们可以在多个线程上划分工作并在最后使用相同的代码减少每个单独的结果这可以产生更好的性能。另外,出于效率原因,我确实选择了结构而不是类,匿名类型默认实现为类,因此您的答案可能不如我的有效;)
      • 好吧,根据*.com/questions/2132615/… 你告诉他你接受他的回答。发生了什么?
      【解决方案3】:
      void Summarize(IEnumerable<Section> sections)
      {
          this.DateBegin = sections.Select(s => s.Date).Min();
          this.DateEnd = sections.Select(s => s.Date).Max();
          this.Income = sections.Where(s => s.IsIncome).Sum(r => r.Amount);
          this.Expenditure = sections.Aggregate(0, (agg, next) => 
              agg += (next.IsExpenditureBank ? next.Amount : 0) +
                  (next.IsExpenditureClient ? next.Amount : 0));
      }
      

      怎么样?

      编辑:

      好的,我重新考虑了一下,看看:

      void Summarize(IEnumerable<Section> sections)
      {
          var result = sections.Aggregate(new
          {
              DateBegin = DateTime.MaxValue,
              DateEnd = DateTime.MinValue,
              Income = 0,
              Expenditure = 0
          },
              (agg, next) =>
              new
              {
                  DateBegin = next.Date < agg.DateBegin ? next.Date : agg.DateBegin,
                  DateEnd = next.Date > agg.DateEnd ? next.Date : agg.DateEnd,
                  Income = agg.Income + (next.IsIncome ? next.Amount : 0),
                  Expenditure = agg.Expenditure + (next.IsExpenditureBank ? next.Amount : 0) +
                      (next.IsExpenditureClient ? next.Amount : 0)
              }
          );
      }
      

      如果sections 为空,请注意错误。

      【讨论】: