Какой из них Вы предпочитаете?
foreach(var zombie in zombies)
{
zombie.ShuffleTowardsSurvivors();
zombie.EatNearbyBrains();
}
или
zombies.Each(zombie => {
zombie.ShuffleTowardsSurvivors();
zombie.EatNearbyBrains();
});
Вторая форма.
На мой взгляд, чем меньше языковых конструкций и ключевых слов нужно использовать, тем лучше. В C# и так достаточно посторонней грубости.
Вообще-то, чем меньше нужно набрать, тем лучше. Серьезно, как можно не использовать "var" в подобных ситуациях? Конечно, если бы это было вашей единственной целью, вы бы все равно использовали венгерскую нотацию... у вас есть IDE, которая дает вам набирать информацию всякий раз, когда вы навешиваете курсор... или, конечно, Ctrl+Q, если вы используете Resharper...
@T.E.D. Последствия вызова делегата для производительности являются вторичными. Если вы делаете это наверняка на тысячу терминов, запустите точечную трассировку и посмотрите, не приемлемо ли это.
@Reed Copsey: re non-standard, если разработчик не может разобраться, что ".каждый" делает, то у вас больше проблем. Взлом языка для того, чтобы сделать его приятнее - одна из величайших радостей программирования.
Первый. Это часть языка неспроста.
Лично я бы использовал второй, функциональный подход к управлению потоком, только если для этого есть веская причина, например, использование Parallel.ForEach
в .NET 4. У него много недостатков, в том числе:
foreach (..) {myDelegate (); }
Я не вижу причин писать собственный синтаксис для конструкции управления потоком, которая уже существует в языке.
Здесь Вы делаете очень важные вещи, такие как написание оператора, а не выражения (так как предположительно каждый метод
не возвращает значения) и мутирующее состояние (что можно только предполагать, так как методы также не возвращают значения), но при этом Вы пытаетесь выдать их за "функциональное программирование", передавая набор операторов в качестве делегата. Этот код едва ли может быть далек от идеалов и идиом функционального программирования, так зачем же пытаться замаскировать его как таковой?
Как бы мне не нравились многопарадигмные языки, такие как C#, я думаю, что их проще всего понять и поддержать, когда парадигмы смешиваются на более высоком уровне (например, целый метод, написанный либо в функциональном, либо в императивном стиле), а не когда несколько парадигм смешиваются в рамках одного высказывания или выражения.
Если вы пишете императивный код, просто будьте честны и используйте цикл. Тут нечего стыдиться. Императивный код не является по своей природе плохой вещью.
Версия лямда на самом деле не медленнее. Я только что сделал быстрый тест и делегатская версия примерно на 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));
Хорошо, итак, я провел еще несколько тестов и вот результаты.
Порядок выполнения (форах, лямбда или лямбда, форах) не сильно отличался, лямбда-версия все равно была быстрее:
форах - 00:00:00.561 лямбда - 00:00:00.389 лямбда - 00:00:00.317 foreach - 00:00:00.337
Разница в производительности намного меньше для массивов классов. Вот числа для Blah[30000000]:
лямбда - 00:00:00.317 foreach - 00:00:00.337
Вот тот же самый тест, но Бла, будучи struct:
Blah[] версией лямбда - 00:00:00.676 форах - 00:00:00.437 Версия списка: лямбда - 00:00:00.461 foreach - 00:00:00.391
Оптимизированная сборка, Blah - это структура, использующая массив.
лямбда - 00:00:00.426 Форах - 00:00:00.079
Вывод: Нет полного ответа на вопрос об исполнении фораха против лямбды. Ответ: Зависит от . Здесь является более научным тестом для List
. Насколько я могу судить, он чертовски эффективен. Если вас действительно волнует использование производительности для(int i...
петлю. Для итерации над коллекцией из тысячи клиентских записей (пример) это действительно не так уж и важно.
Что касается решения о том, какую версию использовать, я бы поставил потенциальный хит производительности для лямбда-версии внизу.
Вывод #2 T[]
(где T - тип значения) цикл форекс примерно в 5 раз быстрее для этого теста в оптимизированной сборке. Это единственное существенное различие между Debug и Release сборкой. Итак, вот так, для массивов типов значений используется форач, все остальное - неважно.
Этот вопрос содержит полезное обсуждение, а также ссылку на пост в блоге MSDN, посвящённый философским аспектам темы.
Я думаю, что методы расширения крутые, но я думаю, что break
и edit-and-continue круче.
Я бы подумал, что вторую форму будет сложнее оптимизировать, так как для этого одного вызова компилятор не сможет развернуть цикл иначе, чем для другого вызова метода Every.
Раз уж его спросили, я подробнее расскажу. Реализация метода вполне может быть скомпилирована отдельно от кода, который на него ссылается. Это значит, что компилятор не знает точно, сколько циклов ему придётся выполнять.
Если использовать форму "foreach", то эта информация может быть доступна компилятору при создании кода для цикла (она также может быть недоступна, в этом случае никакой разницы нет).
Например, если компилятор случайно узнает (из предыдущего кода в том же файле), что в списке ровно 20 элементов, он может заменить весь цикл на 20 ссылок.
Однако, когда компилятор создает код для метода "Каждый" в своем исходном файле, он понятия не имеет, насколько большим будет список вызывающих. Он должен поддерживать любой размер. Лучшее, что он может сделать, это попытаться найти какой-нибудь оптимальный вариант разворачивания для своего процессора, а также добавить дополнительный код в цикл через , который и сделать правильный цикл, если он слишком мал для разворачивания. Для типичного небольшого цикла это может закончиться даже тем, что будет медленнее . Конечно, для маленьких циклов это не так важно....unless они случайно окажутся внутри большого цикла.
Как упоминалось на другом плакате, это (и должно быть) второстепенная проблема. Важно то, что его легче читать и/или поддерживать, но я не вижу здесь большой разницы.
Я тоже не предпочитаю, потому что считаю ненужным использование "var". Я бы написал так:
foreach(Zombie zombie in zombies){
}
Но что касается Функционального или Форач, то для меня, скорее всего, я предпочитаю Форач, потому что для последнего, похоже, нет веской причины.