foreach против суммы для нескольких полей - PullRequest
3 голосов
/ 09 сентября 2011

У меня есть класс, определенный как

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?

Ответы [ 6 ]

4 голосов
/ 09 сентября 2011

Иногда мы, разработчики, нервничаем из-за глупостей.Я был там слишком много.Я не думаю, что есть какая-либо причина, чтобы изменить это.

Тем не менее, если вы хотите, вот идея:

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 );
3 голосов
/ 09 сентября 2011

Если не доказано, что это два медленных, мне нравится

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

Поскольку это облегчает перемещение кода и код рефлектора .

Объединение многих операций в один и тот же цикл может быть быстрее (но обычно вам не важна скорость), однако это усложняет извлечение кода в отдельный метод, который является основой для большинства рефакторинга.

Помните, что в наши дни считывание данных из основной памяти в кэш-память процессора занимает гораздо больше времени (часто в 100 и более раз), а затем процессору требуется считывать данные, уже находящиеся в его кеше. . На 2-й итерации одни и те же данные часто занимают гораздо меньше времени, чем первая.

Будьте очень осторожны, если вы используете профилировщик, так как большинство профилировщиков не воспринимают эффект кеша процессора, профилировщики также заполняют кэш процессора своим кодом времени.

3 голосов
/ 09 сентября 2011

Если в вашем случае важны характеристики, и вы собираетесь повторно использовать сумму двух значений, вы можете написать метод расширения:

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, на мой взгляд, приветствуется.

2 голосов
/ 09 сентября 2011

Может быть, то же самое foreach (хотя для меня это выглядит довольно неплохо), но написанное методом List.ForEach подойдет:

myp.children.ForEach(p => { myp.Val1 += p.Val1; myp.Val2 += p.Val2; });
1 голос
/ 09 сентября 2011

Я думаю, для каждого это хорошо.Одна итерация и просто читать и понимать.Но ниже приведен странный код, который делает это, используя Sum.

myp.Val1 = myp.children.Sum(p => { myp.Val2 += p.Val2; return p.Val1; });
1 голос
/ 09 сентября 2011

Мне кажется, что первый метод чище, более читабелен и эффективен (дважды по списку и создание 2-х лямд в секунду)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...