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 вы могли бы указать ...

206
2

  • Я предлагаю прочитать [блог Эрика Липперса «foreach» против «ForEach»] (http://blogs.msdn.com/ ...
13
Общее количество ответов: 13

Ответ #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 вы не пропустите ни одного элемента.

158
7

  • С 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_iterate break или 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# вы хотите от цикла.

85
0

Ответ #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 очевидными две вещи:

  1. Нам возвращается объект с отслеживанием состояния, хорошо осведомленный о базовой коллекции.
  2. Копия коллекции является неглубокой копией.

Это, конечно, никоим 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 теперь вы знаете, и он может быть информированным.

19
0

Ответ #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 разницу или не увидите никакой разницы.

18
2

  • Начиная с .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 цента

16
1

  • Я думаю, он имел в виду, что механизм выполнения может распараллеливать его автоматически. В противном случае и 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

15
1

  • Проблема, которую вы упомянули с помощью `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 форме.

6
0

Ответ #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 здесь:

https://stackoverflow.com/a/529197/3043

5
0

Ответ #9

Ответ на вопрос: foreach против someList.ForEach() {}

Вся область ForEach (функция делегата) обрабатывается 9X_kotlin-generics как одна строка кода (вызывающая функцию), и 9X_dot-net вы не можете устанавливать точки останова 9X_java-generics или переходить в код. Если возникает необработанное 9X_c-sharp исключение, помечается весь блок.

3
0

Ответ #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

2
0

Ответ #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) => 

2
0

Ответ #12

Ответ на вопрос: foreach против someList.ForEach() {}

Второй способ, который вы показали, использует 9X_csharp метод расширения для выполнения метода делегата 9X_dot-net для каждого из элементов в списке.

Таким 9X_looping образом, у вас есть вызов другого делегата 9X_generic (= метода).

Кроме того, есть возможность 9X_csharp перебирать список с помощью цикла for.

1
0

Ответ #13

Ответ на вопрос: foreach против someList.ForEach() {}

Следует опасаться того, как выйти из метода 9X_dotnet Generic .ForEach - см. this discussion. Хотя ссылка вроде 9X_dot-net и говорит, что это самый быстрый способ. Не 9X_c-sharp уверен, почему - можно подумать, что они 9X_dotnet будут эквивалентны после компиляции ...

1
0