使用LINQ的多个SUM

Ale*_*dro 10 c# linq aggregate-functions

我有一个像下面的循环,我可以使用多个SUM做同样的事情吗?

foreach (var detail in ArticleLedgerEntries.Where(pd => pd.LedgerEntryType == LedgerEntryTypeTypes.Unload &&
                                                                pd.InventoryType == InventoryTypes.Finished))
{
     weight += detail.GrossWeight;
     length += detail.Length;
     items  += detail.NrDistaff;
}
Run Code Online (Sandbox Code Playgroud)

jri*_*sta 8

从技术上讲,你所拥有的可能是做你要求的最有效的方法.但是,您可以在IEnumerable <T>上创建一个名为Each的扩展方法,这可能会使其更简单:

public static class EnumerableExtensions
{
    public static void Each<T>(this IEnumerable<T> col, Action<T> itemWorker)
    {
        foreach (var item in col)
        {
            itemWorker(item);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

并称之为:

// Declare variables in parent scope
double weight;
double length;
int items;

ArticleLedgerEntries
    .Where(
        pd => 
           pd.LedgerEntryType == LedgerEntryTypeTypes.Unload &&
           pd.InventoryType == InventoryTypes.Finished
    )
    .Each(
        pd => 
        {
            // Close around variables defined in parent scope
            weight += pd.GrossWeight; 
            lenght += pd.Length;
            items += pd.NrDistaff;
        }
    );
Run Code Online (Sandbox Code Playgroud)

更新:只需一个额外的说明.上面的例子依赖于一个闭包.变量weight,length和items应该在父作用域中声明,允许它们在每次调用itemWorker操作之后保持不变.为了清楚起见,我已经更新了示例以反映这一点.

  • +1,很好的答案.注意,*MoreLinq*已经有一个`ForEach`扩展方法,为`IEnumerable <T>`声明.另一个注意事项,Eric Lippert [不喜欢这个想法](http://blogs.msdn.com/b/ericlippert/archive/2009/05/18/foreach-vs-foreach.aspx) (2认同)

SLa*_*aks 5

您可以调用Sum3次,但是会慢一些,因为它将进行3次循环。

例如:

var list = ArticleLedgerEntries.Where(pd => pd.LedgerEntryType == LedgerEntryTypeTypes.Unload
                                   && pd.InventoryType == InventoryTypes.Finished))

var totalWeight = list.Sum(pd => pd.GrossWeight);
var totalLength = list.Sum(pd => pd.Length);
var items = list.Sum(pd => pd.NrDistaff); 
Run Code Online (Sandbox Code Playgroud)

由于执行延迟,它也会Where每次都重新评估调用,尽管在您的情况下这不是问题。可以通过调用来避免这种情况ToArray,但这将导致数组分配。(它仍然会运行三个循环)

但是,除非您有大量的条目或在紧密的循环中运行此代码,否则您不必担心性能。


编辑:如果您真的想使用LINQ,则可能会滥用Aggregate,如下所示:

int totalWeight, totalLength, items;

list.Aggregate((a, b) => { 
    weight += detail.GrossWeight;
    length += detail.Length;
    items  += detail.NrDistaff;
    return a;
});
Run Code Online (Sandbox Code Playgroud)

这是非常丑陋的代码,但是执行起来几乎和直线循环一样好。

您也可以求和累加器(请参见下面的示例),但这将为列表中的每个项目分配一个临时对象,这是一个愚蠢的主意。(匿名类型是不可变的)

var totals = list.Aggregate(
    new { Weight = 0, Length = 0, Items = 0},
    (t, pd) => new { 
        Weight = t.Weight + pd.GrossWeight,
        Length = t.Length + pd.Length,
        Items = t.Items + pd.NrDistaff
    }
);
Run Code Online (Sandbox Code Playgroud)