При разработке и рефакторинге процедур во встроенном языке 1С разработчики нередко сталкиваются с логической ошибкой, возникающей при копировании блоков циклов. Рассмотрим классический пример такой ситуации:
Для Каждого Строка1 Из Объект.Состав Цикл
Сообщить(Строка1.Количество);
КонецЦикла;
Для Каждого Строка2 Из Объект.Состав Цикл
Сообщить(Строка1.Количество);
КонецЦикла;
Во втором цикле по невнимательности происходит обращение к переменной Строка1 вместо Строка2. При этом синтаксический контроль платформы 1С не выдает никаких ошибок, а в процессе выполнения второй цикл многократно обрабатывает последнюю строку из первого цикла. Разберем механику этой проблемы, стандарты разработки и инструменты, позволяющие гарантированно исключить подобные дефекты.
Проанализируем причину, по которой платформа 1С считает данный код полностью корректным. Во встроенном языке 1С отсутствует блочная область видимости переменных (блоками являются циклы, условия и т. д.). Все локальные переменные, объявленные явно или неявно в теле метода, имеют единую область видимости — весь контекст процедуры или функции.
Переменная-итератор цикла Для Каждого регистрируется в таблице символов текущего метода. При завершении цикла переменная не уничтожается сборщиком мусора и не сбрасывается в Неопределено, а продолжает хранить ссылку на элемент последней итерации. Если коллекция для обхода оказалась пустой, переменная сохраняет то значение, которое имела до входа в цикл.
Выясним основные подходы к оформлению циклов, которые защищают от подобных накладок:
Строка1, Строка2 или Эл1 провоцируют ошибки из-за визуальной схожести. Имена итераторов должны отражать суть обрабатываемых данных, например: СтрокаСостава, ТекНоменклатура, ЭлементСписка.Неопределено перед началом цикла:
СтрокаСостава = Неопределено;
Для Каждого СтрокаСостава Из Объект.Состав Цикл
// Обработка данных
КонецЦикла;
Иногда программисты пытаются использовать переменную цикла после оператора Прервать, чтобы получить найденный элемент. Стандарты разработки фирмы «1С» прямо запрещают полагаться на значение переменной-итератора за пределами цикла. Для сохранения найденного результата предписывается использовать отдельную переменную:
НайденнаяСтрока = Неопределено;
Для Каждого ТекСтрока Из Объект.Состав Цикл
Если ТекСтрока.Количество = 0 Тогда
НайденнаяСтрока = ТекСтрока;
Прервать;
КонецЕсли;
КонецЦикла;
Если НайденнаяСтрока <> Неопределено Тогда
// Дальнейшая работа с найденным объектом
КонецЕсли;
Поскольку стандартная проверка синтаксиса в конфигураторе 1С пропускает такие ситуации, для их массового поиска и предотвращения в производственных проектах применяются специализированные методики и инструменты:
Строка2 объявляется при обходе коллекции, но нигде не считывается. Анализаторы кода, среды разработки (например, 1C:Enterprise Development Tools / EDT) и языковые серверы мгновенно фиксируют предупреждение об объявленной, но неиспользуемой локальной переменной.Для Каждого, считывается ниже по коду вне границ своего цикла, и выдают предупреждение о потенциальной логической ошибке.