C# - разница между "foreach" и методом расширения: ForEach

Кто-нибудь может указать на различия между C# заявления и их аналогичные методы расширения? например: foreach против .ForEach(метод расширения).

Если есть какая-то разница, что они? Мудрость безопасности? Производительность мудрая? Какой из них лучше использовать? Какой из них безопаснее? и т.п.

И если нет различий, то зачем их писать?

Я долго думал над этим вопросом, если мой, и не нашел своего ответа.

3 ответа

Решение

Это зависит от реализации используемого вами метода расширения. Внутри нет ничего особенного в большинстве версий.ForEach.

Было бы минимальное / незначительное время для загрузки метода расширения при загрузке приложения и времени компиляции. Может потребоваться минимальные накладные расходы для преобразования синтаксиса.ForEach в базовый foreach, поскольку технически это только оболочка. Это может потенциально вызвать проблемы с безопасностью, но только потому, что может создать ситуации закрытия, когда ваши объекты не могут быть собраны в ожидаемое время (например, удерживаться в области действия дольше). В конечном счете, разница очень и очень мала, и все сводится к вкусу. Если, конечно, вы пытаетесь сбривать каждую миллисекунду, и в этом случае лучше всего использовать нативное тело.

Я хотел бы отметить, что.ForEach идет вразрез с предпосылкой использования лямбда-операторов, которые являются чисто функциональными, то есть нарушают "функциональный" стиль и вводят возможность побочных эффектов. Использование тела foreach делает код более читабельным и явным.

Пожалуйста, смотрите: Почему в IEnumerable отсутствует метод расширения ForEach?

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

Взято отсюда

Вторая причина заключается в том, что это добавляет ноль новых репрезентативных возможностей для языка. Это позволяет вам переписать этот совершенно чистый код:

foreach (Foo foo в foos){утверждение, связанное с foo; }

в этот код:

foos.ForEach ((Foo foo) => {оператор, включающий foo; });

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

Предоставленные ответы неточны. При использовании ForEachметод расширения. Например, следующий метод расширения может легко стать убийцей производительности:

      public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
    foreach (var item in source)
    {
        action(item);
    }
}

А потом мы неправильно его используем:

      IEnumerable<T> items = new List<T>();
items.ForEach(UpdateItem);

Красиво выглядит, правда? Что ж, здесь метод расширения вызывается на IEnumerable<T>что означает, что компилятор вынужден выделять общий перечислитель вместо использования оптимизированной версии без выделения памяти . Затем ActionАргумент требует еще одного довольно тяжелого распределения делегатов . Поместите эту петлю на горячую дорожку и Garbage Collector будет сходить с ума, вызывая серьезные проблемы с производительностью.

Пожалуйста, посмотрите мой другой ответ , где я объясню это более подробно.

Что касается безопасности, я видел, как разработчики случайно включали стороннюю сборку для использования определенного ForEach()метод расширения. Это подразумевало отправку нежелательной зависимости от неизвестных источников с неизвестными возможностями.

Резюме

  • безопаснее.
  • более производительный.
  • foreachлучше. Компилятор точно знает, как с этим справиться.

.ForEach похож на Parallel.ForEach, Я видел регулярный .ForEach раньше разрабатывал / отлаживал параллельные версии. Что приятно, так это то, что вам не нужно менять кучу кода для перемещения между ними.

В общем, если у меня нет намерений делать Parallel.ForEachтогда я предпочитаю регулярные foreach для удобочитаемости.

Другие вопросы по тегам