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
для удобочитаемости.