foreach против someList.ForEach() {}
Очевидно, существует множество способов 9X_c#.net перебора коллекции. Любопытно, есть ли какие-то 9X_iterate различия или почему вы бы использовали один 9X_loop способ вместо другого.
Первый тип:
List someList = foreach(string s in someList) { }
Другой 9X_c# способ:
List someList = someList.ForEach(delegate(string s) { });
Я предполагаю, что вместо анонимного 9X_c# делегата, который я использовал выше, у 9X_dotnet вас был бы многоразовый делегат, который 9X_.net вы могли бы указать ...
- Я предлагаю прочитать [блог Эрика Липперса «foreach» против «ForEach»] (http://blogs.msdn.com/ ...
Ответ #1
Ответ на вопрос: foreach против someList.ForEach() {}
Есть одно важное и полезное различие между 9X_csharp ними.
Поскольку .ForEach использует цикл 9X_loop for
для итерации коллекции, это допустимо (редактировать: до .net 4.5 - реализация 9X_dot-net изменилась, и они оба выбрасывают):
someList.ForEach(x => { if(x.RemoveMe) someList.Remove(x); });
тогда 9X_looping как foreach
использует перечислитель, поэтому это 9X_csharp недопустимо:
foreach(var item in someList) if(item.RemoveMe) someList.Remove(item);
tl; dr: НЕ копируйте этот код в свое приложение!
Эти примеры не являются наилучшей 9X_visual-c# практикой, они просто демонстрируют различия 9X_csharp между ForEach()
и foreach
.
Удаление элементов из списка в 9X_.net-framework цикле for
может иметь побочные эффекты. Самый 9X_generic распространенный из них описан в комментариях 9X_c#.net к этому вопросу.
Как правило, если вы хотите 9X_.net удалить несколько элементов из списка, вам 9X_java-generics нужно разделить определение элементов, которые 9X_enumeration нужно удалить, из фактического удаления. Это 9X_loop не делает ваш код компактным, но гарантирует, что 9X_generics вы не пропустите ни одного элемента.
- С Linq все можно сделать лучше. Я просто ...
Ответ #2
Ответ на вопрос: foreach против someList.ForEach() {}
У нас здесь был код (в VS2005 и C# 2.0), где 9X_c#.net предыдущие инженеры старались изо всех сил 9X_generics использовать list.ForEach( delegate(item) { foo;});
вместо foreach(item in list) {foo; };
для всего кода, который 9X_c-sharp они написали. например блок кода для чтения 9X_c# строк из DataReader.
Я до сих пор не знаю, почему 9X_dot-net они это сделали.
Недостатки list.ForEach()
:
-
В C# 2.0 он 9X_loops более подробный. Однако, начиная с C# 3, вы 9X_.net-framework можете использовать синтаксис «
=>
» для создания 9X_kotlin-generics некоторых красиво лаконичных выражений. -
Это 9X_iterate менее знакомо. Люди, которым приходится 9X_.net поддерживать этот код, будут удивляться, почему 9X_c#.net вы так поступили. Мне потребовалось некоторое 9X_c-sharp время, чтобы решить, что для этого нет никакой 9X_loop причины, кроме, может быть, чтобы автор 9X_loop выглядел умным (качество остальной части 9X_loop кода подорвало это). Кроме того, он был 9X_c#.net менее читабельным из-за «
})
» в конце блока 9X_generic кода делегата. -
См. также книгу Билла Вагнера 9X_c# «Эффективный C#: 50 конкретных способов 9X_enumeration улучшить свой C#», где он говорит о том, почему 9X_.net foreach предпочтительнее других циклов, таких 9X_c-sharp как циклы for или while - главное, что вы 9X_visual-c# позволяете компилятору решить, как лучше 9X_enumeration всего построить цикл. Если в будущей версии 9X_c# компилятора удастся использовать более быстрый 9X_c# метод, вы получите его бесплатно, используя 9X_loops foreach и перестроение, а не изменяя свой 9X_c-sharp код.
-
конструкция
foreach(item in list)
позволяет использовать 9X_iteratebreak
илиcontinue
, если вам нужно выйти из итерации 9X_c# или цикла. Но вы не можете изменить список 9X_kotlin-generics внутри цикла foreach.
Я удивлен, увидев, что 9X_dotnet list.ForEach
работает немного быстрее. Но это, вероятно, не 9X_c# веская причина использовать его повсюду, это 9X_visual-c# было бы преждевременной оптимизацией. Если 9X_iterate ваше приложение использует базу данных или 9X_c#.net веб-службу, которая, а не управление циклом, почти 9X_generics всегда будет там, где идет время. Вы также 9X_c#.net сравнивали его с циклом for
? list.ForEach
мог бы быть быстрее 9X_loops из-за внутреннего использования, а цикл 9X_c#.net for
без оболочки был бы еще быстрее.
Я не согласен 9X_loops с тем, что версия list.ForEach(delegate)
является "более функциональной" в 9X_csharp каком-либо значительном смысле. Он передает 9X_c-sharp функцию функции, но нет большой разницы 9X_enumeration в результате или организации программы.
Я 9X_.net не думаю, что foreach(item in list)
"точно говорит, как вы хотите, чтобы 9X_dot-net это было сделано" - цикл for(int 1 = 0; i < count; i++)
делает это, цикл 9X_c-sharp foreach
оставляет выбор управления компилятором.
Я 9X_.net-framework чувствую, что в новом проекте я использую 9X_c#.net foreach(item in list)
для большинства циклов, чтобы придерживаться 9X_visual-c# обычного использования и для удобства чтения, и 9X_csharp использовать list.Foreach()
только для коротких блоков, когда 9X_enumeration вы можете сделать что-то более элегантно 9X_c# или компактно с помощью оператора C# 3 "=>
". В 9X_visual-c# подобных случаях может уже существовать 9X_enumeration более конкретный метод расширения LINQ, чем 9X_c# ForEach()
. Посмотрите, не выполняет ли Where()
, Select()
, Any()
, All()
, Max()
или 9X_csharp один из многих других методов LINQ то, что 9X_c# вы хотите от цикла.
Ответ #3
Ответ на вопрос: foreach против someList.ForEach() {}
Как говорится, дьявол кроется в деталях 9X_java-generics ...
Самая большая разница между двумя методами 9X_dotnet перечисления коллекций заключается в том, что 9X_enumeration foreach
передает состояние, а ForEach(x => { })
- нет.
Но давайте 9X_csharp копнем немного глубже, потому что есть некоторые 9X_looping вещи, которые вы должны знать, которые могут 9X_loops повлиять на ваше решение, и есть некоторые 9X_c# предостережения, о которых вам следует знать 9X_java-generics при кодировании для любого случая.
Давайте 9X_looping использовать List
в нашем небольшом эксперименте, чтобы 9X_c-sharp наблюдать за поведением. Для этого эксперимента 9X_generics я использую .NET 4.7.2:
var names = new List { "Henry", "Shirley", "Ann", "Peter", "Nancy" };
Давайте переберем 9X_c# это сначала с помощью foreach
:
foreach (var name in names) { Console.WriteLine(name); }
Мы могли бы расширить 9X_c# это до:
using (var enumerator = names.GetEnumerator()) { }
С перечислителем в руках, заглянув 9X_c-sharp под обложку, мы получим:
public List.Enumerator GetEnumerator() { return new List.Enumerator(this); } internal Enumerator(List list) { this.list = list; this.index = 0; this.version = list._version; this.current = default (T); } public bool MoveNext() { List list = this.list; if (this.version != list._version || (uint) this.index >= (uint) list._size) return this.MoveNextRare(); this.current = list._items[this.index]; ++this.index; return true; } object IEnumerator.Current { { if (this.index == 0 || this.index == this.list._size + 1) ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen); return (object) this.Current; } }
Сразу становятся 9X_loop очевидными две вещи:
- Нам возвращается объект с отслеживанием состояния, хорошо осведомленный о базовой коллекции.
- Копия коллекции является неглубокой копией.
Это, конечно, никоим 9X_csharp образом не является потокобезопасным. Как 9X_looping было указано выше, изменение коллекции во 9X_kotlin-generics время итерации - это просто плохой моджо.
Но 9X_c# как насчет проблемы с тем, что коллекция 9X_generics становится недействительной во время итерации 9X_enumeration из-за того, что мы не используем ее во время 9X_java-generics итерации? Передовой опыт предлагает управление 9X_iterate версиями коллекции во время операций и итераций, а 9X_java-generics также проверку версий, чтобы определить, когда 9X_enumeration изменяется базовая коллекция.
Здесь все становится 9X_c# действительно неясным. Согласно документации 9X_c-sharp Microsoft:
Если в коллекцию вносятся изменения, например 9X_loops добавление, изменение или при удалении 9X_.net элементов поведение перечислителя не определено.
Что 9X_dotnet это значит? Например, то, что List
реализует 9X_c#.net обработку исключений, не означает, что все 9X_java-generics коллекции, реализующие IList
, будут делать то 9X_looping же самое. Похоже, это явное нарушение принципа 9X_generics замены Лискова:
Объекты суперкласса могут 9X_c# быть заменены объектами его подклассы 9X_c-sharp без нарушения работы приложения.
Другая проблема 9X_.net заключается в том, что перечислитель должен 9X_.net реализовать IDisposable
- это означает еще один источник 9X_looping потенциальных утечек памяти, не только если 9X_csharp вызывающий ошибается, но и если автор неправильно 9X_dotnet реализует шаблон Dispose
.
Наконец, у нас есть проблема 9X_csharp времени жизни ... что произойдет, если итератор 9X_c# действителен, но основная коллекция исчезла? Теперь 9X_csharp мы сделаем снимок того, что было ... когда вы разделяете 9X_generics время жизни коллекции и ее итераторы, вы 9X_kotlin-generics напрашиваетесь на проблемы.
Теперь рассмотрим 9X_.net-framework ForEach(x => { })
:
names.ForEach(name => { });
Расширяется до:
public void ForEach(Action action) { if (action == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match); int version = this._version; for (int index = 0; index < this._size && (version == this._version || !BinaryCompatibility.TargetsAtLeast_Desktop_V4_5); ++index) action(this._items[index]); if (version == this._version || !BinaryCompatibility.TargetsAtLeast_Desktop_V4_5) return; ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion); }
Важное примечание:
for (int index = 0; index < this._size && ... ; ++index) action(this._items[index]);
Этот код 9X_dotnet не выделяет счетчиков (ничего для Dispose
) и не 9X_enumeration приостанавливает во время итерации.
Обратите внимание, что 9X_loops при этом также выполняется неглубокая копия 9X_dot-net базовой коллекции, но теперь коллекция является 9X_iterate моментальным снимком во времени. Если автор 9X_c# неправильно реализует проверку на изменение 9X_generic коллекции или ее «устаревание», снимок по-прежнему 9X_java-generics действителен.
Это никоим образом не защищает 9X_dotnet вас от проблемы с жизненным циклом ... если 9X_.net-framework основная коллекция исчезает, теперь у вас 9X_.net-framework есть неглубокая копия, указывающая на то, что 9X_looping было ... но, по крайней мере, у вас нет 9X_kotlin-generics проблема Dispose
, которую нужно решать на потерянных 9X_dot-net итераторах ...
Да, я сказал итераторы ... иногда 9X_c#.net полезно иметь состояние. Предположим, вы 9X_visual-c# хотите сохранить что-то вроде курсора базы 9X_dot-net данных ... может быть, можно использовать 9X_c# несколько стилей foreach
Iterator
. Мне лично не нравится 9X_visual-c# этот стиль дизайна, так как существует слишком 9X_iterate много жизненных проблем, и вы полагаетесь 9X_looping на милость авторов коллекций, на которые 9X_looping вы полагаетесь (если вы буквально не пишете 9X_loop все сами с нуля).
Всегда есть третий вариант 9X_dotnet ...
for (var i = 0; i < names.Count; i++) { Console.WriteLine(names[i]); }
Это не сексуально, но в нем есть зубы 9X_enumeration (извинения перед Томом Крузом и фильмом Фирма)
Это ваш выбор, но 9X_.net теперь вы знаете, и он может быть информированным.
Ответ #4
Ответ на вопрос: foreach против someList.ForEach() {}
Ради интереса я вставил List в отражатель 9X_c# и получил C#:
public void ForEach(Action action) { if (action == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match); } for (int i = 0; i < this._size; i++) { action(this._items[i]); } }
Точно так же MoveNext в перечислителе, который 9X_kotlin-generics используется foreach, выглядит следующим 9X_java-generics образом:
public bool MoveNext() { if (this.version != this.list._version) { ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion); } if (this.index < this.list._size) { this.current = this.list._items[this.index]; this.index++; return true; } this.index = this.list._size + 1; this.current = default(T); return false; }
List.ForEach гораздо более урезан, чем 9X_c#.net MoveNext - гораздо меньше обработки - с 9X_dot-net большей вероятностью JIT превратится во 9X_c-sharp что-то эффективное.
Кроме того, foreach() будет 9X_dot-net выделять новый перечислитель, несмотря ни 9X_loops на что. GC - ваш друг, но если вы делаете 9X_visual-c# один и тот же foreach несколько раз, это 9X_c-sharp приведет к появлению большего количества 9X_.net-framework одноразовых объектов, в отличие от повторного 9X_iterate использования одного и того же делегата 9X_csharp - НО - это действительно второстепенный случай. При 9X_c-sharp типичном использовании вы увидите небольшую 9X_enumeration разницу или не увидите никакой разницы.
- Начиная с .NET Core 3.1 ForEach по-прежнему работает ...
Ответ #5
Ответ на вопрос: foreach против someList.ForEach() {}
Думаю, вызов someList.ForEach()
можно было бы легко распараллелить, тогда 9X_iterate как обычный foreach
не так-то просто запустить 9X_c-sharp параллельно. Вы можете легко запустить несколько 9X_loop разных делегатов на разных ядрах, что не 9X_kotlin-generics так просто сделать с обычным foreach
.
Только мои 9X_generics 2 цента
- Я думаю, он имел в виду, что механизм выполнения может распараллеливать его автоматически. В противном случае и foreach, и .ForEach можно распаралл ...
Ответ #6
Ответ на вопрос: foreach против someList.ForEach() {}
Я знаю две непонятные вещи, которые их отличают. Иди 9X_enumeration ко мне!
Во-первых, есть классическая ошибка 9X_csharp создания делегата для каждого элемента в 9X_loop списке. Если вы используете ключевое слово 9X_enumeration foreach, все ваши делегаты могут в конечном 9X_dot-net итоге обращаться к последнему элементу списка:
// A list of actions to execute later List actions = new List(); // Numbers 0 to 9 List numbers = Enumerable.Range(0, 10).ToList(); // Store an action that prints each number (WRONG!) foreach (int number in numbers) actions.Add(() => Console.WriteLine(number)); // Run the actions, we actually print 10 copies of "9" foreach (Action action in actions) action(); // So try again actions.Clear(); // Store an action that prints each number (RIGHT!) numbers.ForEach(number => actions.Add(() => Console.WriteLine(number))); // Run the actions foreach (Action action in actions) action();
Метод 9X_kotlin-generics List.ForEach не имеет этой проблемы. Текущий 9X_java-generics элемент итерации передается по значению 9X_.net в качестве аргумента внешней лямбда, а затем 9X_.net внутренняя лямбда правильно захватывает 9X_generics этот аргумент в своем собственном замыкании. Проблема 9X_enumeration решена.
(К сожалению, я считаю, что ForEach 9X_visual-c# является членом List, а не методом расширения, хотя 9X_looping его легко определить самостоятельно, так 9X_loops что у вас есть эта возможность для любого 9X_c-sharp перечислимого типа.)
Во-вторых, подход метода 9X_generics ForEach имеет ограничение. Если вы реализуете 9X_c#.net IEnumerable с помощью yield return, вы не 9X_csharp можете сделать return return внутри лямбда. Таким 9X_c#.net образом, цикл по элементам в коллекции с 9X_java-generics целью получения возвращаемых вещей с помощью 9X_.net-framework этого метода невозможен. Вам нужно будет 9X_c#.net использовать ключевое слово foreach и обойти 9X_looping проблему закрытия, вручную сделав копию 9X_csharp текущего значения цикла внутри цикла.
More here
- Проблема, которую вы упомянули с помощью `foreach`,« исправлена »в C# 5. http://stackoverflow.co ...
Ответ #7
Ответ на вопрос: foreach против someList.ForEach() {}
Вы можете назвать анонимного делегата :-)
А 9X_visual-c# вторую можно написать как:
someList.ForEach(s => s.ToUpper())
Что я предпочитаю, и 9X_c# избавляет от лишнего набора текста.
Как говорит 9X_enumeration Иоахим, параллелизм легче применить ко второй 9X_dot-net форме.
Ответ #8
Ответ на вопрос: foreach против someList.ForEach() {}
List.ForEach() считается более функциональным.
List.ForEach()
говорит, что 9X_kotlin-generics вы хотите сделать. foreach(item in list)
также говорит, как именно 9X_loop вы хотите, чтобы это было сделано. Это оставляет 9X_iterate List.ForEach
свободным изменять реализацию части как в 9X_csharp будущем. Например, гипотетическая будущая 9X_generics версия .Net может всегда запускать List.ForEach
параллельно, при 9X_loops условии, что в этот момент у каждого есть 9X_kotlin-generics несколько ядер процессора, которые обычно 9X_loops простаивают.
С другой стороны, foreach (item in list)
дает вам 9X_loop немного больше контроля над циклом. Например, вы 9X_generic знаете, что элементы будут повторяться в 9X_dot-net каком-то последовательном порядке, и вы 9X_generic можете легко разорвать середину, если элемент 9X_.net-framework соответствует некоторому условию.
Некоторые 9X_c# недавние замечания по этому поводу доступны 9X_enumeration здесь:
Ответ #9
Ответ на вопрос: foreach против someList.ForEach() {}
Вся область ForEach (функция делегата) обрабатывается 9X_kotlin-generics как одна строка кода (вызывающая функцию), и 9X_dot-net вы не можете устанавливать точки останова 9X_java-generics или переходить в код. Если возникает необработанное 9X_c-sharp исключение, помечается весь блок.
Ответ #10
Ответ на вопрос: foreach против someList.ForEach() {}
За кулисами анонимный делегат превращается 9X_looping в реальный метод, поэтому у вас могут возникнуть 9X_visual-c# некоторые накладные расходы на второй вариант, если 9X_c#.net компилятор не решил встроить функцию. Кроме 9X_.net того, любые локальные переменные, на которые 9X_.net ссылается тело примера анонимного делегата, изменились 9X_enumeration бы по своей природе из-за уловок компилятора, скрывающих 9X_loops тот факт, что он компилируется в новый метод. Подробнее 9X_iterate о том, как C# делает это чудо, читайте здесь:
http://blogs.msdn.com/oldnewthing/archive/2006/08/04/688527.aspx
Ответ #11
Ответ на вопрос: foreach против someList.ForEach() {}
Функция ForEach является членом универсального 9X_loop класса List.
Я создал следующее расширение 9X_.net для воспроизведения внутреннего кода:
public static class MyExtension { public static void MyForEach(this IEnumerable collection, Action action) { foreach (T item in collection) action.Invoke(item); } }
В конце 9X_iterate мы используем обычный foreach (или цикл 9X_java-generics for, если хотите).
С другой стороны, использование 9X_enumeration функции делегата - это просто еще один способ 9X_loop определения функции, этот код:
delegate(string s) { }
эквивалентно:
private static void myFunction(string s, ) { }
или 9X_.net используя выражения labda:
(s) =>
Ответ #12
Ответ на вопрос: foreach против someList.ForEach() {}
Второй способ, который вы показали, использует 9X_csharp метод расширения для выполнения метода делегата 9X_dot-net для каждого из элементов в списке.
Таким 9X_looping образом, у вас есть вызов другого делегата 9X_generic (= метода).
Кроме того, есть возможность 9X_csharp перебирать список с помощью цикла for.
Ответ #13
Ответ на вопрос: foreach против someList.ForEach() {}
Следует опасаться того, как выйти из метода 9X_dotnet Generic .ForEach - см. this discussion. Хотя ссылка вроде 9X_dot-net и говорит, что это самый быстрый способ. Не 9X_c-sharp уверен, почему - можно подумать, что они 9X_dotnet будут эквивалентны после компиляции ...
-
31
-
14
-
2
-
8
-
5
-
4
-
8
-
10
-
10
-
5
-
2
-
8
-
3
-
8
-
2
-
5
-
7
-
3
-
2
-
3
-
23
-
8
-
33
-
14
-
8
-
5
-
10
-
8
-
6
-
6
-
12
-
2
-
6
-
7
-
5
-
1
-
5
-
3
-
5
-
3
-
5
-
6
-
7
-
8
-
2
-
3
-
2
-
3
-
8
-
2