К статье
Практический материал

Разработка и Конфигурация · 10 мин

Как работает цикл «Для каждого» в 1С

Как работает цикл «Для каждого … Цикл» в 1С: синтаксис, обход коллекций, типичные ошибки, отличия от «Для … По» и практические примеры кода с пояснениями.

Цикл «Для каждого … Цикл» — основной способ последовательного обхода элементов коллекции во встроенном языке 1С. Он применяется к массивам, таблицам значений, выборкам из запросов, спискам значений, строкам табличных частей документов и другим коллекциям, которые поддерживают итерацию. На каждом шаге цикла в переменную помещается очередной элемент, и тело цикла выполняется ровно столько раз, сколько элементов содержит коллекция.

Отличие от классического цикла «Для … По» принципиально: разработчик не управляет индексом вручную и не следит за границами коллекции. Платформа сама проходит по всем элементам от первого до последнего. Это убирает целый класс ошибок — выход за границы массива, пропуск первого или последнего элемента, бесконечный цикл из-за неверного инкремента.

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

Синтаксис и порядок выполнения цикла

Общая форма конструкции выглядит так:

Для каждого Строка Из ТаблицаТоваров Цикл

// тело цикла: работа со Строка

КонецЦикла;

Здесь Строка — переменная цикла, которая на каждой итерации получает очередной элемент коллекции ТаблицаТоваров. Имя переменной выбирает разработчик; хорошая практика — называть её по смыслу элемента (СтрокаТовара, ЭлементСпр, ВыборкаДокумент), а не абстрактно.

Порядок выполнения: платформа проверяет, есть ли в коллекции следующий элемент. Если есть — присваивает его переменной цикла и выполняет тело. Если элементов больше нет — управление переходит на оператор после КонецЦикла. Если коллекция пуста, тело цикла не выполнится ни разу, и это не считается ошибкой.

Переменная цикла сохраняет значение последнего элемента после завершения обхода. Полагаться на это в логике не стоит, но при отладке такое поведение полезно знать.

С какими коллекциями работает «Для каждого»

Конструкция применима к большинству перечислимых объектов платформы. Наиболее частые случаи:

  • Массив — обход в порядке индексов от нулевого к последнему;
  • Таблица значений и табличная часть — обход строк сверху вниз;
  • Выборка из результата запроса — обход записей в порядке, заданном запросом;
  • Список значений — обход элементов с доступом к значению и представлению;
  • Дерево значений — обход строк одного уровня (для вложенных уровней нужен рекурсивный обход);
  • Соответствие и структура — обход пар «ключ-значение» через объект КлючИЗначение.

Пример обхода соответствия:

Для каждого КлючЗначение Из СоответствиеКурсов Цикл

Сообщить(КлючЗначение.Ключ); // валюта

Сообщить(КлючЗначение.Значение); // курс

КонецЦикла;

Важно понимать, что порядок обхода соответствия и структуры не гарантированно совпадает с порядком добавления элементов. Если порядок критичен, используйте массив или таблицу значений.

Чем «Для каждого» отличается от «Для … По»

Цикл со счётчиком «Для Сч = 0 По Массив.Количество() — 1 Цикл» даёт доступ к индексу и позволяет идти в обратном порядке или с шагом. «Для каждого» таких возможностей не предоставляет — только прямой последовательный обход.

Сравнение вариантов

Для каждого

Чтение и обработка всех элементов коллекции. Короче и безопаснее, нет риска ошибок с индексами. Не подходит для удаления элементов и обратного обхода.

Для … По со счётчиком

Нужен индекс элемента, обход в обратном порядке, обработка части коллекции или удаление элементов с конца. Требует аккуратности с границами.

Практическое правило: если индекс не нужен — пишите «Для каждого». Код получается короче, читается легче, а вероятность ошибки «на единицу» исчезает. Счётчик оправдан, когда нужно знать позицию элемента, сравнивать соседние элементы или удалять строки во время обхода.

Изменение коллекции внутри цикла: что можно и что нельзя

Ключевое ограничение: нельзя добавлять или удалять элементы коллекции, которую обходите, внутри цикла «Для каждого». Платформа в таких случаях либо выдаёт ошибку «Коллекция была изменена», либо поведение становится непредсказуемым — часть элементов пропускается.

При этом изменять свойства текущего элемента можно и нужно. Например, обход строк табличной части с пересчётом суммы — типовой и безопасный сценарий:

Для каждого СтрокаТовара Из Объект.Товары Цикл

СтрокаТовара.Сумма = СтрокаТовара.Количество * СтрокаТовара.Цена;

КонецЦикла;

Если удалить строки всё-таки нужно, есть два рабочих приёма. Первый — собрать строки к удалению в отдельный массив внутри «Для каждого», а удалять уже после цикла. Второй — обход таблицы с конца циклом со счётчиком, где удаление текущей строки не сдвигает ещё не обработанные элементы.

Управление выполнением: Прервать и Продолжить

Оператор Прервать досрочно завершает цикл — управление переходит на строку после КонецЦикла. Типовой сценарий — поиск первого подходящего элемента: нашли, запомнили, прервали. Дальнейший обход бессмысленен и только тратит время.

Оператор Продолжить пропускает остаток тела текущей итерации и переходит к следующему элементу. С его помощью удобно отфильтровывать неподходящие строки в начале цикла, не наращивая вложенность условий:

Для каждого Строка Из ТаблицаОстатков Цикл

Если Строка.Количество = 0 Тогда

Продолжить;

КонецЕсли;

// обработка только ненулевых остатков

КонецЦикла;

Оба оператора действуют только на тот цикл, внутри которого записаны. При вложенных циклах «Прервать» остановит лишь внутренний, внешний продолжит работу — это частый источник недоразумений. Если нужно прервать оба уровня, используйте признак-флаг или выносите логику в функцию с «Возврат».

Порядок действий при написании цикла
  1. 1
    Определите, какую коллекцию нужно обойти, и убедитесь, что она заполнена (проверьте Количество() при необходимости)
  2. 2
    Дайте переменной цикла осмысленное имя элемента, а не абстрактное
  3. 3
    В начале тела отсеките неподходящие элементы через Продолжить
  4. 4
    Выполните обработку элемента, изменяя только его свойства, а не состав коллекции
  5. 5
    Если цель достигнута досрочно — добавьте Прервать
  6. 6
    Проверьте результат на коллекции из 0, 1 и нескольких элементов

Обход выборки из запроса

Один из самых частых сценариев в прикладном коде — обход результата запроса. Здесь «Для каждого» работает с выборкой, и важно помнить: выборка читается последовательно, вернуться назад по ней нельзя.

Выборка = Запрос.Выполнить().Выбрать();

Пока Выборка.Следующий() Цикл

// обработка записи

КонецЦикла;

Обратите внимание: для выборки из запроса чаще используют конструкцию «Пока Выборка.Следующий() Цикл», а не «Для каждого». Оба варианта работают, но «Пока» — устоявшийся стиль для выборок, тогда как «Для каждого» применяют к выгруженным таблицам значений: Результат.Выгрузить() возвращает таблицу, которую удобно обходить именно «Для каждого».

Если данных много, выгрузка всей таблицы в память может быть избыточной — обход выборки через «Следующий()» экономичнее. Для небольших результатов разница неощутима, выбирайте по удобству кода.

Производительность и типичные ошибки

Сам по себе «Для каждого» — быстрая конструкция; проблемы производительности почти всегда связаны с тем, что делается внутри цикла. Главный антипаттерн — запросы к базе данных в теле цикла. Сто итераций со ста запросами внутри дают сто обращений к СУБД, тогда как один запрос с соединением таблиц решает задачу за одно обращение.

Вторая частая проблема — поиск методом «Найти» внутри цикла по большой таблице значений. Каждый вызов перебирает таблицу линейно, и на больших объёмах обход превращается в квадратичный по времени. Решение — заранее построить соответствие для быстрого поиска по ключу или использовать индексы таблицы значений.

Типичные ошибки
01
Удаление строк таблицы внутри цикла «Для каждого» — ошибка выполнения или пропуск элементов; удаляйте после цикла или с конца счётчиком
02
Запрос к базе внутри цикла — резкое падение производительности; выносите данные в один запрос с соединениями
03
Ожидание, что Прервать остановит вложенные циклы целиком — прерывается только текущий уровень
04
Использование результата обхода без проверки пустой коллекции в логике после цикла — переменные-признаки остаются неинициализированными
05
Изменение переменной цикла с расчётом, что это повлияет на обход, — состав коллекции от этого не меняется

Для проверки корректности цикла достаточно трёх контрольных случаев: пустая коллекция, один элемент, несколько элементов. Если код работает на всех трёх, большинство граничных ошибок исключено.

Контрольные точки

Как проверить результат

Пустая коллекция
Норма

Тело цикла не выполняется, код после цикла отрабатывает корректно

Тревожный признак

Ошибка обращения к переменным, которые заполняются только внутри цикла

Один элемент
Норма

Тело выполняется ровно один раз, результат соответствует ожиданию

Тревожный признак

Двойная обработка или пропуск единственного элемента

Условие Прервать
Норма

Цикл останавливается на нужном элементе, последующие не обрабатываются

Тревожный признак

Обход продолжается до конца, лишняя нагрузка

Частые вопросы

Можно ли узнать номер текущей итерации в цикле «Для каждого»?

Встроенного счётчика у конструкции нет. Если номер нужен, заведите переменную перед циклом и увеличивайте её вручную в начале тела, либо используйте цикл «Для … По» с индексом.

Что произойдёт, если коллекция пустая?

Тело цикла не выполнится ни разу, ошибки не возникнет. Но убедитесь, что код после цикла корректно работает в ситуации, когда ни один элемент не был обработан, — например, итоговая сумма осталась нулевой.

Можно ли обойти табличную часть документа циклом «Для каждого»?

Да, табличная часть — полноценная коллекция строк. Обход используется для пересчёта сумм, проверки заполнения, подстановки значений. Изменять реквизиты текущей строки можно, удалять строки внутри цикла — нельзя.

Что быстрее: «Для каждого» или «Для … По»?

Разница в скорости самих конструкций несущественна и не должна влиять на выбор. Выбирайте по задаче: «Для каждого» — для чтения и обработки всех элементов, счётчик — когда нужен индекс, обратный порядок или удаление элементов.

Как обойти дерево значений со всеми вложенными уровнями?

«Для каждого» обходит только один уровень строк. Для полного обхода используют рекурсивную процедуру: она обходит строки текущего уровня и вызывает сама себя для коллекции «Строки» каждой строки.

Итог

Что важно запомнить

«Для каждого … Цикл» — стандартный способ обхода коллекций в 1С: короткий, безопасный и читаемый. Изменяйте внутри цикла только свойства элементов, а не состав коллекции; удаляйте строки после цикла или счётчиком с конца; выносите запросы к базе из тела цикла. Проверяйте код на пустой, одноэлементной и обычной коллекции.