用linq在聚合函数之间切换

时间:2010-08-17 20:41:11

标签: c# linq aggregate-functions

我有两个看起来几乎相同的方法,除了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 个答案:

答案 0 :(得分:8)

尝试以下

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));

答案 1 :(得分:2)

在里面添加选择器很麻烦,但最终你可以使用Func<IEnumerable<decimal>,decimal>,并传入Enumerable.MaxEnumerable.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))));
    ...
}

答案 2 :(得分:1)

让人惊讶:

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);
嗯,不要这样做,做JaredPar说的......

或者,如果您仍想获得此语法,请使用一些严重的别名。

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”。

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