Рефакторинг кода для преобразования цикла for в оператор foreach и наоборот
Область применения:
Visual Studio Visual Studio для Mac
Visual Studio Code ![]()
В этой статье описан рефакторинг кода с помощью быстрых действий, который позволяет перейти с одной структуры выполнения цикла на другую. Здесь также описано, почему в некоторых случаях не стоит преобразовывать цикл for в оператор foreach в коде.
Преобразование цикла for в оператор foreach
Если у вас есть цикл for в коде, можно использовать этот рефакторинг кода для преобразования цикла в оператор foreach.
Область применения этого рефакторинга:
Рефакторинг с помощью быстрого действия Преобразовать в foreach доступен только для циклов for, которые содержат все три части: инициализатор, условие и итератор.
Для чего это нужно
Причины, из-за которых может потребоваться преобразование цикла for в оператор foreach, включают в себя:
- Вы используете локальную переменную внутри цикла только в качестве индекса для доступа к элементу.
- Вы хотите упростить код и уменьшить вероятность возникновения логических ошибок в инициализаторе, условиях и разделах итератора.
Использование
- Поместите курсор на ключевое слово for .
- Нажмите клавиши CTRL+ или щелкните значок отвертки
в поле файла кода. 
- Выберите Преобразовать в foreach. Также можно выбрать Просмотр изменений, чтобы открыть диалоговое окно Просмотр изменений, и нажать Применить.
Преобразование оператора foreach в цикл for
Если у вас есть оператор foreach (C#) или For Each. Next (Visual Basic) в коде, их можно преобразовать в цикл for с помощью этого рефакторинга кода.
Область применения этого рефакторинга:
Для чего это нужно
Причины, из-за которых может потребоваться преобразование оператора foreach в цикл for, включают в себя:
- Вы хотите использовать локальную переменную внутри цикла не только для доступа к элементу.
- Вы перебираете элементы многомерного массива и вам требуется больший контроль над элементами массива.
Использование
- Поместите курсор на ключевое слово foreach или For Each .
- Нажмите клавиши CTRL+ или щелкните значок отвертки
в поле файла кода. 
- Выберите Преобразовать в for. Также можно выбрать Просмотр изменений, чтобы открыть диалоговое окно Просмотр изменений, и нажать Применить.
- Так как в данном случае рефакторинг кода порождает новую переменную — счетчик итераций, в правом верхнем углу редактора появится поле Переименовать. Если необходимо выбрать другое имя для переменной, введите его и нажмите клавишу ВВОД или кнопку Применить в поле Переименовать. Если вы не хотите изменять имя, нажмите клавишу ESC или кнопку Применить, чтобы закрыть поле Переименовать.
Код C#, созданный в процессе выполнения рефакторинга, использует явный тип или ключевое слово var для этого типа элементов в коллекции. Тип в созданном коде (явный или неявный) зависит от параметров стиля кода, которые находятся в области. Эти параметры стиля кода настраиваются на уровне компьютера в разделе >»Параметры>текстового редактора текста» C#>Code Style>General’var> или на уровне решения в файле EditorConfig.> Если вы измените эти параметры в меню Параметры, снова откройте файл кода, чтобы изменения вступили в силу.
См. также
- Рефакторинг
- Просмотр изменений
Как цикл foreach поменять на обычный for?
Строго говоря, заменить foreach на доступ по индексу(порядковому номеру) можно не для всех коллекций, например Set не поддерживает такого доступа в принципе. С другой стороны нужно помнить, что некоторые коллекции, например LinkedList при индексном доступе тратят O(n) времени для доступа к n -му элементу коллекции! Итерировать коллекцию по индексу наподобие carPool.get(i); следует только, если коллекция поддерживает доступ по индексу за константное время ( O(1) ). Например классы, реализующие интерфейс RandomAccess предоставляют такой доступ.
Поэтому для задач, где есть необходимость знать индекс текущего элемента и не поддерживающих доступ по индексу или требующих линейного времени доступа к элементу по индексу, следует использовать итератор, а номер индекса считать в отдельной переменной, например вот так:
int i = 0; for (Iterator iter = collection.iterator(); iter.hasNext(); i++)
Отслеживать
ответ дан 13 июл 2016 в 7:00
Mikhailov Valentin Mikhailov Valentin
1,980 12 12 серебряных знаков 21 21 бронзовый знак
Логично, да. В .NET, кстати, есть интерфейс IList
13 июл 2016 в 7:11
13 июл 2016 в 8:38
Ага, круто, спасибо.
13 июл 2016 в 8:48
@VladD все же прямым аналогом IList
Array.prototype.forEach()
Метод forEach() выполняет указанную функцию один раз для каждого элемента в массиве.
Интерактивный пример
Синтаксис
arr.forEach(function callback(currentValue, index, array) < //your iterator >[, thisArg]);
Параметры
Функция, которая будет вызвана для каждого элемента массива. Она принимает от одного до трёх аргументов:
Текущий обрабатываемый элемент в массиве.
Индекс текущего обрабатываемого элемента в массиве.
Массив, по которому осуществляется проход.
Необязательный параметр. Значение, используемое в качестве this при вызове функции callback .
Возвращаемое значение
Описание
Метод forEach() выполняет функцию callback один раз для каждого элемента, находящегося в массиве в порядке возрастания. Она не будет вызвана для удалённых или пропущенных элементов массива. Однако, она будет вызвана для элементов, которые присутствуют в массиве и имеют значение undefined .
Функция callback будет вызвана с тремя аргументами:
- значение элемента (value)
- индекс элемента (index)
- массив, по которому осуществляется проход (array)
Если в метод forEach() был передан параметр thisArg , при вызове callback он будет использоваться в качестве значения this . В противном случае, в качестве значения this будет использоваться значение undefined . В конечном итоге, значение this , наблюдаемое из функции callback , определяется согласно обычным правилам определения this , видимого из функции .
Диапазон элементов, обрабатываемых методом forEach() , устанавливается до первого вызова функции callback . Элементы, добавленные в массив после начала выполнения метода forEach() , не будут посещены функцией callback . Если существующие элементы массива изменятся, значения, переданные в функцию callback , будут значениями на тот момент времени, когда метод forEach() посетит их; удалённые элементы посещены не будут. Если уже посещённые элементы удаляются во время итерации (например, с помощью shift() ), последующие элементы будут пропущены. ( Смотри пример ниже )
Примечание: Не существует способа остановить или прервать цикл forEach() кроме как выбрасыванием исключения. Если вам необходимо такое поведение, метод forEach() неправильный выбор.
Досрочное прекращение может быть достигнуто с:
- Простой цикл for
- Циклы for. of / for. in
- Array.prototype.every()
- Array.prototype.some()
- Array.prototype.find()
- Array.prototype.findIndex()
Если нужно протестировать элементы массива на условие и нужно вернуть булево значение, вы можете воспользоваться методами every() , some() , find() или findIndex() .
Метод forEach() выполняет функцию callback один раз для каждого элемента массива; в отличие от методов every() и some() , он всегда возвращает значение undefined .
Примеры
Нет операции для неинициализированных значений (разреженные массивы)
const arraySparse = [1, 3, , 7]; let numCallbackRuns = 0; arraySparse.forEach((element) => console.log(element); numCallbackRuns++; >); console.log("numCallbackRuns: ", numCallbackRuns); // 1 // 3 // 7 // numCallbackRuns: 3 // комментарий: как вы видите пропущенное значение между 3 и 7 не вызывало функцию callback.
Конвертируем цикл for в forEach
const items = ["item1", "item2", "item3"]; const copy = []; // до for (let i = 0; i items.length; i++) copy.push(items[i]); > // после items.forEach(function (item) copy.push(item); >);
Печать содержимого массива
Примечание: Для отображения содержимого массива в консоли вы можете использовать console.table() , который выводит отформатированную версию массива.
Следующий пример иллюстрирует альтернативный подход, использующий forEach() .
Следующий код выводит каждый элемент массива на новой строке журнала:
function logArrayElements(element, index, array) console.log("a[" + index + "] token operator">+ element); > // Обратите внимание на пропуск по индексу 2, там нет элемента, поэтому он не посещается [2, 5, , 9].forEach(logArrayElements); // логи: // a[0] = 2 // a[1] = 5 // a[3] = 9
Использование thisArg
Следующий (надуманный) пример обновляет свойства объекта, когда перебирает записи массива:
function Counter() this.sum = 0; this.count = 0; > Counter.prototype.add = function (array) array.forEach((entry) => this.sum += entry; ++this.count; >, this); // ^---- Note >; const obj = new Counter(); obj.add([2, 5, 9]); obj.count; // 3 obj.sum; // 16
Поскольку в forEach() передан параметр thisArg ( this ), он затем передаётся в callback при каждом вызове. И callback использует его в качестве собственного значения this .
Примечание: Если при передаче callback функции используется выражение стрелочной функции , параметр thisArg может быть опущен, так как все стрелочные функции лексически привязываются к значению this .
Прерывание цикла
Следующий код использует Array.prototype.every() для логирования содержимого массива и останавливается при превышении значением заданного порогового значения THRESHOLD .
var THRESHOLD = 12; var v = [5, 2, 16, 4, 3, 18, 20]; var res; res = v.every(function (element, index, array) console.log("element:", element); if (element >= THRESHOLD) return false; > return true; >); console.log("res:", res); // логи: // element: 5 // element: 2 // element: 16 // res: false res = v.some(function (element, index, array) console.log("element:", element); if (element >= THRESHOLD) return true; > return false; >); console.log("res:", res); // логи: // element: 5 // element: 2 // element: 16 // res: true
Функция копирования объекта
Следующий код создаёт копию переданного объекта. Существует несколько способов создания копии объекта, и это один из них. Он позволяет понять, каким образом работает Array.prototype.forEach() , используя функции мета-свойств Object.* из ECMAScript 5.
function copy(o) var copy = Object.create(Object.getPrototypeOf(o)); var propNames = Object.getOwnPropertyNames(o); propNames.forEach(function (name) var desc = Object.getOwnPropertyDescriptor(o, name); Object.defineProperty(copy, name, desc); >); return copy; > var o1 = a: 1, b: 2 >; var o2 = copy(o1); // теперь o2 выглядит также, как и o1
Модификация массива во время итерации
В следующем примере в лог выводится «one» , «two» , «four» .
При достижении записи, содержащей значение ‘two’ , первая запись всего массива удаляется, в результате чего все оставшиеся записи перемещаются на одну позицию вверх. Поскольку элемент ‘four’ теперь находится на более ранней позиции в массиве, ‘three’ будет пропущен.
forEach() не делает копию массива перед итерацией.
let words = ["one", "two", "three", "four"]; words.forEach((word) => console.log(word); if (word === "two") words.shift(); > >); // one // two // four
Выравнивание (уплощение) массива
Следующий пример приведён только для целей обучения. Если вы хотите выравнять массив с помощью встроенных методов, вы можете использовать Array.prototype.flat()
function flatten(arr) const result = []; arr.forEach((i) => if (Array.isArray(i)) result.push(. flatten(i)); > else result.push(i); > >); return result; > // Usage const nested = [1, 2, 3, [4, 5, [6, 7], 8, 9]]; flatten(nested); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
Полифил
Метод forEach() был добавлен к стандарту ECMA-262 в 5-м издании; поэтому он может отсутствовать в других реализациях стандарта. Вы можете работать с ним, добавив следующий код в начало ваших скриптов, он позволяет использовать forEach() в реализациях, которые не поддерживают этот метод. Этот алгоритм является точно тем, что описан в ECMA-262 5-го издания; он предполагает, что Object и TypeError имеют свои первоначальные значения и что callback.call вычисляется в оригинальное значение Function.prototype.call() .
// Шаги алгоритма ECMA-262, 5-е издание, 15.4.4.18 // Ссылка (en): http://es5.github.io/#x15.4.4.18 // Ссылка (ru): http://es5.javascript.ru/x15.4.html#x15.4.4.18 if (!Array.prototype.forEach) Array.prototype.forEach = function (callback, thisArg) var T, k; if (this == null) throw new TypeError(" this is null or not defined"); > // 1. Положим O равным результату вызова ToObject passing the |this| value as the argument. var O = Object(this); // 2. Положим lenValue равным результату вызова внутреннего метода Get объекта O с аргументом "length". // 3. Положим len равным ToUint32(lenValue). var len = O.length >>> 0; // 4. Если IsCallable(callback) равен false, выкинем исключение TypeError. // Смотрите: http://es5.github.com/#x9.11 if (typeof callback !== "function") throw new TypeError(callback + " is not a function"); > // 5. Если thisArg присутствует, положим T равным thisArg; иначе положим T равным undefined. if (arguments.length > 1) T = thisArg; > // 6. Положим k равным 0 k = 0; // 7. Пока k < len, будем повторятьwhile (k len) var kValue; // a. Положим Pk равным ToString(k). // Это неявное преобразование для левостороннего операнда в операторе in // b. Положим kPresent равным результату вызова внутреннего метода HasProperty объекта O с аргументом Pk. // Этот шаг может быть объединён с шагом c // c. Если kPresent равен true, то if (k in O) // i. Положим kValue равным результату вызова внутреннего метода Get объекта O с аргументом Pk. kValue = O[k]; // ii. Вызовем внутренний метод Call функции callback с объектом T в качестве значения this и // списком аргументов, содержащим kValue, k и O. callback.call(T, kValue, k, O); > // d. Увеличим k на 1. k++; > // 8. Вернём undefined. >; >
Спецификации
| Specification |
|---|
| ECMAScript Language Specification # sec-array.prototype.foreach |
Совместимость с браузерами
BCD tables only load in the browser
Смотрите также
- Array.prototype.find()
- Array.prototype.findIndex()
- Array.prototype.map()
- Array.prototype.every()
- Array.prototype.some()
- Map.prototype.forEach() (en-US)
- Set.prototype.forEach()
Как заменить foreach на for c

В C# массив объявляется следующим образом:

Примечание
Индексация массивов в C# начинается с 0. То есть первый элемент массива имеет индекс 0. Если размер массива равен 5, то индекс последнего элемента будет равен 4.
Доступ к элементам массива
Для получения доступа к элементам массива используется индекс массива.

Строки < 1, 2, 3 >и < 4, 5, 6 >– это элементы 2D-массива.
Объявление двумерного массива
Вот как объявляется двумерный массив в C#:

Ключевое слово in используется вместе с циклом foreach для перебора элементов iterable-item . in выбирает элемент из iterable-item на каждой итерации и сохраняет в переменной element .
На первой итерации первый элемент iterable-item сохраняется в element . На второй итерации второй элемент сохраняется в element и т. д.
Число выполнений цикла foreach равно количеству элементов в массиве или коллекции.
Вот пример перебора массива с помощью цикла for :
Вывод массива используя цикл for
using System; namespace Loop < class ForLoop < public static void Main(string[] args) < char[] myArray = ; for(int i = 0; i < myArray.Length; i++) < Console.WriteLine(myArray[i]); >> > >
Эта же задача решается с помощью цикла foreach .
Вывод массива используя цикл foreach
using System; namespace Loop < class ForEachLoop < public static void Main(string[] args) < char[] myArray = ; foreach(char ch in myArray) < Console.WriteLine(ch); >> > >
После запуска обеих программ вывод будет таким:
В приведенной выше программе цикл foreach перебирает массив myArray . На первой итерации первый элемент, т. е. myArray[0] выбирается и сохраняется в ch .
Аналогично, на последней итерации выбирается последний элемент, т. е. myArray[4] . Внутри тела цикла выводится значение ch .
При рассмотрении обеих программ, программа, использующая цикл foreach , выглядит удобочитаемой и легкой в понимании. Это связано с простотой и выразительностью синтаксиса foreach .
Следующая программа подсчитывает количество кандидатов мужского и женского пола.
Обход массива полов с использованием цикла foreach
using System; namespace Loop < class ForEachLoop < public static void Main(string[] args) < char[] gender = ; int male = 0, female = 0; foreach (char g in gender) < if (g == 'm') male++; else if (g =='f') female++; >Console.WriteLine("Number of male = ", male); Console.WriteLine("Number of female = ", female); > > >
Number of male = 6
Number of female = 4
Следующая программа подсчитывает сумму элементов в List .
Цикл foreach с List (список или коллекция)
using System; using System.Collections.Generic; namespace Loop < class ForEachLoop < public static void Main(string[] args) < var numbers = new List() < 5, -8, 3, 14, 9, 17, 0, 4 >; int sum = 0; foreach (int number in numbers) < sum += number; >Console.WriteLine("Sum = ", sum); Console.ReadLine(); > > >
В этой программе цикл foreach используется для обхода коллекции. Обход коллекции похож на обход массива.
Первый элемент коллекции выбирается на первой итерации, второй элемент на второй и т. д. до последнего элемента.
Из этой статьи мы узнали
- Как работать с одномерными, многомерными и ступенчатыми массивами.
- Как работать с циклом foreach .
- О пространстве имен System.Linq и несколько примеров работы с ним.
Материалы по теме
- ️ Самоучитель по C# для начинающих. Часть 1: установите среду разработки и освойте основы языка за 30 минут
- ❓ Как использовать обобщения в C# – краткая инструкция для новичков
- 8 шагов от новичка до профессионала: дорожная карта разработчика на C#