2010-08-17 2 views
2

У меня есть два метода, которые выглядят почти одинаковыми, за исключением агрегатной функции, используемой в запросе Linq. Например:Переключение между агрегатными функциями с помощью 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) в качестве параметров. Как я могу это достичь?

ответ

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

Добавление селектора внутрь - это боль, но в конечном итоге вы можете взять 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)))); 
    ... 
} 
1

Yikes !:

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».

Мы все еще находимся на пути к кроличьей дыре, поэтому это не то решение, которое вы ищете. Просто поставьте здесь для демонстрационных целей :)

+0

+1 - Хотя мне понравилось, подпись метода становится слишком трудной для чтения, даже с псевдонимами. – Fernando

Смежные вопросы