C# foreach по сравнению с функциональным каждый [закрылся]

Какой из них Вы предпочитаете?

foreach(var zombie in zombies)
{
    zombie.ShuffleTowardsSurvivors();
    zombie.EatNearbyBrains();
}

или

zombies.Each(zombie => {
    zombie.ShuffleTowardsSurvivors();
    zombie.EatNearbyBrains();
});
19
задан whatupdave 7 January 2010 в 22:55
поделиться

8 ответов

Вторая форма.

На мой взгляд, чем меньше языковых конструкций и ключевых слов нужно использовать, тем лучше. В C# и так достаточно посторонней грубости.

Вообще-то, чем меньше нужно набрать, тем лучше. Серьезно, как можно не использовать "var" в подобных ситуациях? Конечно, если бы это было вашей единственной целью, вы бы все равно использовали венгерскую нотацию... у вас есть IDE, которая дает вам набирать информацию всякий раз, когда вы навешиваете курсор... или, конечно, Ctrl+Q, если вы используете Resharper...

@T.E.D. Последствия вызова делегата для производительности являются вторичными. Если вы делаете это наверняка на тысячу терминов, запустите точечную трассировку и посмотрите, не приемлемо ли это.

@Reed Copsey: re non-standard, если разработчик не может разобраться, что ".каждый" делает, то у вас больше проблем. Взлом языка для того, чтобы сделать его приятнее - одна из величайших радостей программирования.

5
ответ дан 30 November 2019 в 03:08
поделиться

Первый. Это часть языка неспроста.

Лично я бы использовал второй, функциональный подход к управлению потоком, только если для этого есть веская причина, например, использование Parallel.ForEach в .NET 4. У него много недостатков, в том числе:

  • Он медленнее. Будет введен вызов делегата для каждого элемента, как вы это сделали foreach (..) {myDelegate (); }
  • Это нестандартно, поэтому большинству разработчиков будет труднее понять
  • Если вы закроете какие-либо локальные переменные, вы заставите компилятор сделать закрытие. Это может привести к странным проблемам, если задействована многопоточность, а также добавляет совершенно ненужное раздувание сборки.

Я не вижу причин писать собственный синтаксис для конструкции управления потоком, которая уже существует в языке.

26
ответ дан 30 November 2019 в 03:08
поделиться

Здесь Вы делаете очень важные вещи, такие как написание оператора, а не выражения (так как предположительно каждый метод не возвращает значения) и мутирующее состояние (что можно только предполагать, так как методы также не возвращают значения), но при этом Вы пытаетесь выдать их за "функциональное программирование", передавая набор операторов в качестве делегата. Этот код едва ли может быть далек от идеалов и идиом функционального программирования, так зачем же пытаться замаскировать его как таковой?

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

Если вы пишете императивный код, просто будьте честны и используйте цикл. Тут нечего стыдиться. Императивный код не является по своей природе плохой вещью.

10
ответ дан 30 November 2019 в 03:08
поделиться

Версия лямда на самом деле не медленнее. Я только что сделал быстрый тест и делегатская версия примерно на 30% быстрее.

Вот код:

class Blah {
    public void DoStuff() {
    }
}

        List<Blah> blahs = new List<Blah>();
        DateTime start = DateTime.Now;

        for(int i = 0; i < 30000000; i++) {
            blahs.Add(new Blah());
        }

        TimeSpan elapsed = (DateTime.Now - start);
        Console.WriteLine(string.Format(System.Globalization.CultureInfo.CurrentCulture, "Allocation - {0:00}:{1:00}:{2:00}.{3:000}",
         elapsed.Hours,
         elapsed.Minutes,
         elapsed.Seconds,
         elapsed.Milliseconds));

        start = DateTime.Now;

        foreach(var bl in blahs) {
            bl.DoStuff();
        }

        elapsed = (DateTime.Now - start);
        Console.WriteLine(string.Format(System.Globalization.CultureInfo.CurrentCulture, "foreach - {0:00}:{1:00}:{2:00}.{3:000}",
         elapsed.Hours,
         elapsed.Minutes,
         elapsed.Seconds,
         elapsed.Milliseconds));

        start = DateTime.Now;

        blahs.ForEach(bl=>bl.DoStuff());

        elapsed = (DateTime.Now - start);
        Console.WriteLine(string.Format(System.Globalization.CultureInfo.CurrentCulture, "lambda - {0:00}:{1:00}:{2:00}.{3:000}",
         elapsed.Hours,
         elapsed.Minutes,
         elapsed.Seconds,
         elapsed.Milliseconds));

Хорошо, итак, я провел еще несколько тестов и вот результаты.

  1. Порядок выполнения (форах, лямбда или лямбда, форах) не сильно отличался, лямбда-версия все равно была быстрее:

    форах - 00:00:00.561
    лямбда - 00:00:00.389
    
    лямбда - 00:00:00.317
    foreach - 00:00:00.337
  2. Разница в производительности намного меньше для массивов классов. Вот числа для Blah[30000000]:

    лямбда - 00:00:00.317 
    foreach - 00:00:00.337
  3. Вот тот же самый тест, но Бла, будучи struct:

    Blah[] версией 
    лямбда - 00:00:00.676 
    форах - 00:00:00.437 
    
    Версия списка:
    лямбда - 00:00:00.461
    foreach - 00:00:00.391
  4. Оптимизированная сборка, Blah - это структура, использующая массив.

    лямбда - 00:00:00.426
    Форах - 00:00:00.079

Вывод: Нет полного ответа на вопрос об исполнении фораха против лямбды. Ответ: Зависит от . Здесь является более научным тестом для List. Насколько я могу судить, он чертовски эффективен. Если вас действительно волнует использование производительности для(int i... петлю. Для итерации над коллекцией из тысячи клиентских записей (пример) это действительно не так уж и важно.

Что касается решения о том, какую версию использовать, я бы поставил потенциальный хит производительности для лямбда-версии внизу.

Вывод #2 T[] (где T - тип значения) цикл форекс примерно в 5 раз быстрее для этого теста в оптимизированной сборке. Это единственное существенное различие между Debug и Release сборкой. Итак, вот так, для массивов типов значений используется форач, все остальное - неважно.

4
ответ дан 30 November 2019 в 03:08
поделиться

Этот вопрос содержит полезное обсуждение, а также ссылку на пост в блоге MSDN, посвящённый философским аспектам темы.

1
ответ дан 30 November 2019 в 03:08
поделиться

Я думаю, что методы расширения крутые, но я думаю, что break и edit-and-continue круче.

1
ответ дан 30 November 2019 в 03:08
поделиться

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


Раз уж его спросили, я подробнее расскажу. Реализация метода вполне может быть скомпилирована отдельно от кода, который на него ссылается. Это значит, что компилятор не знает точно, сколько циклов ему придётся выполнять.

Если использовать форму "foreach", то эта информация может быть доступна компилятору при создании кода для цикла (она также может быть недоступна, в этом случае никакой разницы нет).

Например, если компилятор случайно узнает (из предыдущего кода в том же файле), что в списке ровно 20 элементов, он может заменить весь цикл на 20 ссылок.

Однако, когда компилятор создает код для метода "Каждый" в своем исходном файле, он понятия не имеет, насколько большим будет список вызывающих. Он должен поддерживать любой размер. Лучшее, что он может сделать, это попытаться найти какой-нибудь оптимальный вариант разворачивания для своего процессора, а также добавить дополнительный код в цикл через , который и сделать правильный цикл, если он слишком мал для разворачивания. Для типичного небольшого цикла это может закончиться даже тем, что будет медленнее . Конечно, для маленьких циклов это не так важно....unless они случайно окажутся внутри большого цикла.

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

0
ответ дан 30 November 2019 в 03:08
поделиться

Я тоже не предпочитаю, потому что считаю ненужным использование "var". Я бы написал так:

foreach(Zombie zombie in zombies){
}

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

-4
ответ дан 30 November 2019 в 03:08
поделиться
Другие вопросы по тегам:

Похожие вопросы: