2010-08-17 4 views
2

J'ai deux méthodes qui semblent presque identiques, à l'exception de la fonction d'agrégat utilisée dans la requête Linq. Par exemple:basculer entre les fonctions d'agrégat avec 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))); 
    // ... 
} 

où le Item est une classe comme ceci:

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

Comme les deux méthodes font la même chose, je voudrais avoir juste une méthode où je passe le IEnumerable et la fonction d'agrégation (somme/max) en tant que paramètres. Comment puis-je y parvenir?

Répondre

8

Effectuez les opérations suivantes

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

L'ajout du sélecteur à l'intérieur est pénible, mais vous pouvez finalement prendre un Func<IEnumerable<decimal>,decimal> et transmettre Enumerable.Max ou Enumerable.Sum comme instance de délégué. Pour ce faire sans dupliquer le sélecteur =>r.Value nécessiterait de faire le sélecteur premier, à savoir

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

Hummm, ne pas faites ceci, faites ce que JaredPar a dit ...

Ou si vous voulez toujours obtenir cette syntaxe, utilisez un alias sérieux.

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))); 
    // ... 
} 

Il pourrait presque être lisible si vous pouvez utiliser des alias dans d'autres alias, ou correspondre aux délégués personnalisés avec les signatures correspondant Func, alors vous pourriez bloquer « Selector » là au lieu de « Func ».

Nous sommes encore loin d'un trou de lapin, donc ce n'est pas la solution que vous recherchez. Il suffit de mettre ici à des fins de démonstration :)

+0

+1 - Bien que j'aie aimé, la signature de méthode devient trop difficile à lire, même avec les alias. – Fernando