多个字段的Foreach vs sum

本文关键字:vs sum Foreach 字段 | 更新日期: 2023-09-27 18:08:19

我有一个定义为

的类
class P
{
        public List<P> children; 
        public int Val1;
        public int Val2;
}

可以看到,类有一个相同类的对象列表。我实例化类并填充列表:

P myp = new P { children = new List<P> {new P {Val1 = 1, Val2 = 1}, new P {Val1 = 2, Val2 = 2}}};

现在,如果我想汇总子字段的值并将它们放置在适当的父字段中,我可以执行

foreach (var p in myp.children)
{
        myp.Val1 += p.Val1;
        myp.Val2 += p.Val2;
}

看起来很有效,但是很难看或者我可以写

myp.Val1 = myp.children.Sum(p => p.Val1);
myp.Val2 = myp.children.Sum(p => p.Val2);

更具可读性,但会遍历列表两次。

是否有一种既美观又有效的方法来做到这一点?还是我被困在了foreach里?

多个字段的Foreach vs sum

有时候我们开发人员会对一些最愚蠢的事情感到神经质。我去过那里太多次了。我认为没有任何理由去改变它。

如果你愿意,这里有一个主意:

class P
{
    public List<P> children;
    public int Val1;
    public int Val2;
    public void Add( P p )
    {
        this.Val1 += p.Val1;
        this.Val2 += p.Val2;
    }
}

然后你可以做…

myp.children.ForEach( myp.Add );

如果性能与您的情况相关,并且您要重用两个值的和,您可以编写扩展方法:

public static void SumMultiple<T>(this IEnumerable<T> x, Func<T, int> selector1, Func<T, int> selector2, out int a, out int b)
{
    a = b = 0;
    foreach (var item in x)
    {
        a += selector1(item);
        b += selector2(item);
    }
}

测试代码:

int val1, val2;
collection.SumMultiple(a => a.A, b => b.B, out val1, out val2);
Console.WriteLine("Val1 is {0}, Val2 is {1}", val1, val2);

否则,我认为foreach循环是受欢迎的。

除非它被证明是两个慢我喜欢

myp.Val1 = myp.children.Sum(p => p.Val1); 
myp.Val2 = myp.children.Sum(p => p.Val2); 

使得移动代码和反射器代码更容易。

将许多操作组合到同一个循环中可能会更快,(但通常你不太关心速度),但是它使得将代码提取到一个单独的方法中变得更加困难,而这是大多数重构的基础。

请记住,现在从主存读取数据到处理器缓存需要更长的时间(通常是100倍或更多),然后处理器需要读取已经在其缓存中的数据。对相同的数据进行第二次迭代通常比第一次迭代花费的时间要少得多。

如果您使用分析器,请非常小心,因为大多数分析器不会拾取处理器缓存的效果,分析器也倾向于用它们的计时代码填充处理器缓存。

可能是相同的foreach(看起来对我来说很好),但用列表编写。ForEach方法将做:

myp.children.ForEach(p => { myp.Val1 += p.Val1; myp.Val2 += p.Val2; });

对我来说,第一个方法似乎更干净,更易读,更有效(通过list &创建2个lambda

我想每个都很好。一次迭代,易于阅读和理解。但下面是一个奇怪的代码,它使用Sum来完成。

myp.Val1 = myp.children.Sum(p => { myp.Val2 += p.Val2; return p.Val1; });