使用LINQ在C#中创建自定义聚合函数

时间:2013-12-04 09:56:20

标签: c# linq c#-4.0

考虑以下课程:

public class TaxType
{
    public int Id {get;set;}
    public decimal TotalTaxCollected {get;set;}
    public string DetailXml {get;set;}
}

我在代码中的某处获得了以下LINQ查询:

GetTaxTypesFromTheDataSource()
.Where(/*blah blah blah*/)
.GroupBy(t => new { t.Id })
.Select(g => new TaxType
{
    TotalTaxCollected = g.Sum(n => n.TotalTaxCollected),
    DetailXml = g.Aggregate(SumOfInnerElementsOfXml).DetailXml
})
.ToList();

SumOfInnerElementsOfXml声明如下:

private TaxType SumOfInnerElementsOfXml(TaxType t1, TaxType t2)
{
    //(a) Deserialize the t1.DetailXml & t2.DetailXml into 2 different arrays.
    //(b) Aggregate both arrays based on their IDs.
    //(c) Serialize the result into an xml string.
    //(d) Instantiate a new TaxType and set its DetailXml to the generated xml string.
    //return (d)
}

上面的解决方案工作正常,但我想创建自己的聚合函数,以便我可以按如下方式使用它:

GetTaxTypesFromTheDataSource()
.Where(/*blah blah blah*/)
.GroupBy(t => new { t.Id })
.Select(g => new TaxType
{
    TotalTaxCollected = g.Sum(n => n.TotalTaxCollected),
    DetailXml = g.MyCustomAggregateFunction(n => n.DetailXml)
})
.ToList();

怎么可能?我尝试了几种扩展方法,但不幸的是它们都没有。

1 个答案:

答案 0 :(得分:3)

您的初始解决方案对我来说似乎很好。但是,如果你不喜欢它......

要创建预定义函数(不是lambda),我建议先在查询中删除匿名类型 - 更改:

.GroupBy(t => new { t.Id })

.GroupBy(t => t.Id)

这通常也更好,因为你摆脱了每个TaxType的一个装箱/拆箱并将IGrouping<{anonymous},TaxType>转换为IGrouping<int,TaxType>(如果有人稍后检查代码,则更容易理解语义)

然后,您可以按如下方式声明方法:

public static class Extensions {
    public static string MyCustomAggregateFunction(this IGrouping<int,TaxType> source, Func<TaxType,string> selector) {
        // blah-blah-blah
        //return something
    }
}

如果需要,您可以稍后将其设为通用。

我还建议摆脱对IGrouping的依赖,因为您以后可能需要在其他地方应用该聚合:

TotalTaxCollected = g.Sum(n => n.TotalTaxCollected),
DetailXml = g.Select(n => n.DetailXml).AggregateTaxXML()

public static string AggregateTaxXML(this IEnumerable<string> source) {
    // blah-blah-blah
    //return something
}

来源/进一步阅读:MSDN