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

使用LINQ在聚合函数之间切换

  •  2
  • Fernando  · 技术社区  · 14 年前

    我有两个看起来几乎相同的方法,只是在LINQ查询中使用的聚合函数除外。例如:

    public IEnumerable<Item> DoStuff(IEnumerable<Item> someItems) {
        var items = someItems.GroupBy(i => i.Date).Select(p => new Item(p.Key, p.Sum(r => r.Value)));
        // ...
    }
    
    public IEnumerable<Item> DoOtherStuff(IEnumerable<Item> someItems) {
        var items = someItems.GroupBy(i => i.Date).Select(p => new Item(p.Key, p.Max(r => r.Value)));
        // ...
    }
    

    何处 Item 是这样一个类:

    public class Item {
        public DateTime Date { get; private set; }
        public decimal Value { get; private set; }
    
        public Item(DateTime date, decimal value) {
         Date = date;
         Value = value;
        }
    }
    

    由于这两个方法都执行相同的操作,所以我只希望有一个方法,在其中我将IEnumerable和聚合函数(sum/max)作为参数传递。我怎样才能做到?

    3 回复  |  直到 14 年前
        1
  •  8
  •   JaredPar    14 年前

    尝试以下操作

    public IEnumerable<Item> DoStuff(
      IEnumerable<Item> someItems,
      Func<IGrouping<DateTime,decimal>,Item> reduce) {
      var items = someItems.GroupBy(i => i.Date).Select(reduce);
      ...
    }
    
    DoStuff(someItems, p => p.Sum(r => r.Value));
    DoStuff(someItems, p => p.Max(r => r.Value));
    
        2
  •  2
  •   Marc Gravell    14 年前

    在内部添加选择器是一种痛苦,但最终您可以 Func<IEnumerable<decimal>,decimal> 并通过 Enumerable.Max Enumerable.Sum 作为委托实例。这样做 没有 复制 =>r.Value 选择器需要先执行选择器,即

    // usage:
    DoStuff(items, Enumerable.Sum);
    DoStuff(items, Enumerable.Max);
    // shared method:
    public IEnumerable<Item> DoStuff(IEnumerable<Item> someItems,
            Func<IEnumerable<decimal>,decimal> aggregate)
    {
        var items = someItems.GroupBy(i => i.Date).Select(
            p => new Item(p.Key, aggregate(p.Select(r=>r.Value))));
        ...
    }
    
        3
  •  1
  •   Merlyn Morgan-Graham    14 年前

    伊克斯!:

    public IEnumerable<Item> DoOtherStuff(IEnumerable<Item> someItems,
        Func<
            IGrouping<DateTime, Item>,
            Func<Func<Item, decimal>, decimal>
            > aggregateForGrouping
        )
    {
        var items = someItems.GroupBy(i => i.Date)
            .Select(p => new Item(p.Key, aggregateForGrouping(p)(r => r.Value)));
        // ...
    }
    
    DoOtherStuff(someItems, p => p.Max);
    

    嗯,别这样,照贾雷德帕说的做…

    或者,如果您仍然想使用这种语法,请使用一些严重的别名。

    using ItemGroupingByDate = IGrouping<DateTime, Item>;
    using AggregateItems = Func<Func<Item, decimal>, decimal>;
    
    public IEnumerable<Item> DoOtherStuff(
        IEnumerable<Item> someItems,
        Func<ItemGroupingByDate, AggregateItems> getAggregateForGrouping
        )
    {
        var items = someItems.GroupBy(i => i.Date)
            .Select(p => new Item(p.Key, getAggregateForGrouping(p)(r => r.Value)));
        // ...
    }
    

    如果您可以在其他别名中使用别名,或者将自定义委托与匹配的func签名匹配,那么您可以在其中插入“选择器”而不是“func”,这几乎是可读的。

    不过,我们仍然在一个兔子洞里,所以这不是你想要的解决方案。只是为了演示而放在这里。)