Страницы

Поиск по вопросам

пятница, 27 декабря 2019 г.

Метод ForEach и IEnumerable

#c_sharp #net #linq #дизайн_языка


Подскажите, а в чем мотивация того, что этот метод работает только с List? Ведь
IEnumerable тоже имеет все необходимое.
    


Ответы

Ответ 1



Многие люди спрашивают меня, почему Microsoft не сделал для коллекций метод расширения ForEach. У класса List уже есть такой метод, но почему не добавить такой метод для всех последовательностей. Практически - это однострочник: public static void ForEach(this IEnumerable sequence, Action action) { // argument null checking omitted foreach(T item in sequence) action(item); } Обычно на вопрос: "Почему X не реализовано?" я отвечаю, что любая особенность не реализована, пока кто-то ее не спроектирует, реализует, протестирует, и предоставит. И никто не даст денег на это. И да, хотя я отмечал, что даже самые маленькие особенности могут иметь большую стоимость, конкретно эта по настоящему простая, очевидная, легко тестируемая и легко документируемая. Стоимость всегда имеет значение, но в этом случае она незначительна. С другой стороны, если это так просто, почему бы не сделать это самому, если так нужно? И по настоящему важна тут не низкая стоимость, а полученная выгода. Дальше будет видно, что я считаю выгоду здесь также небольшой, если вообще не отрицательной. Но мы должны посмотреть немного глубже. У меня есть две причины против этого метода. Первая причина заключается в том, что это нарушает принципы функционального программирования, на которых основаны остальные операторы для последовательностей. Очевидно, что цель этого метода - вызвать побочные эффекты. Цель выражения (expression) - вычислить значение, а не получить побочный эффект. Получение побочного эффекта - это цель для statement. Его вызов выглядит как выражение (expression) (хотя, надо признать, что, так как метод возвращает void, выражение может быть использовано только в контексте "statement expression"). Меня не устраивает делать единственный оператор только для последовательностей, который будет использоваться только для побочных эффектов. Вторая причина заключается в том, что это ничего не добавляет языку. И сделав это можно будет переписать совершенно чистую строку кода foreach(Foo foo in foos){ statement involving foo; } в следующую foos.ForEach((Foo foo)=>{ statement involving foo; }); которая использует почти те же символы в немного другом порядке. Кроме того, вторая версия тяжелее для понимания, отладки, вводит семантику замыканий, что в некоторых случаях может поменять время жизни объекта. Когда мы предоставляем два похожих способа сделать одно и то же, мы вносим путаницу в индустрии, становится тяжелее читать код друг друга и т.д. Иногда выгода от наличия двух разный текстовых представлений для одной операции (например, query-синтаксис vs method-синтаксис или + vs String.Concat) настолько огромна, что можно пренебречь потенциальной путаницей. Но убедительное преимущество query выражений - их читаемость; новая форма foreach читается определенно не лучше, а может быть даже и хуже. Если вы не согласны с такими философскими возражениями и видите практическую ценность в таком шаблоне, идите и напишите этот однострочник сами. Перевод блога @EricLippert

Ответ 2



Этот метод нужен только для side-эффектов, что противоречит функциональной концепции linq. Этот метод не ведёт к сокращению кода: foreach(Foo foo in foos){ statement involving foo; } foos.ForEach((Foo foo)=>{ statement involving foo; }); получились почти те же символы, но в несколько другом порядке. К тому же вторую версию сложнее понять, сложнее отладить и в ней появляется замыкание, которое может повлиять на время жизни объектов. Но каждый, кто не согласен с этими философскими причинами и видит пользу в этом паттерне, - вперёд! Просто реализуйте этот тривиальный однострочник самостоятельно. Источник: https://blogs.msdn.microsoft.com/ericlippert/2009/05/18/foreach-vs-foreach/

Ответ 3



До .net 4.5 разница была во внутренней реализации: foreach внутри реализовался(и реализуется) через итератор. Это означает что все завалится как только убрать из списка один элемент. Зато работает быстрее. ForEach() же БЫЛ внутри реализован через for(int i=0; i { if(x.RemoveMe) someList.Remove(x); }); Начиная с .net 4.5 оба метода работают через итератор и являются взаимозаменяемыми. Просто разный синтаксис использования. Информация взята с: https://stackoverflow.com/a/226082/4423545 Так же нашлась статья Eric Lipperts blog "foreach" vs "ForEach" -- а он -- principal developer on the C# compiler team. Думаю, что там можно нарыть какую-то дополнительную информацию. На сколько я понял, ForEach оставили только листу и не привязывали всем IEnumerable что бы не плодить: foos.ForEach((Foo foo) => { statement involving foo; }); // сложно для понимания при беглом взгляде а что бы люди писали: foreach(Foo foo in foos) { statement involving foo; }// легко для понимания И даже в 1 строку читается все равно проще: foreach(Foo foo in foos){ statement involving foo; }

Комментариев нет:

Отправить комментарий