Почему в массиве 1С все элементы становятся одинаковыми и как это исправить?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

Анализ проблемы: ссылочные типы данных

Для начала проанализируем, как 1С работает с объектами типа Структура, Массив, ТаблицаЗначений или Соответствие. Все эти объекты относятся к ссылочным типам. Это означает, что в переменной хранится не само содержимое объекта, а адрес (ссылка) на участок оперативной памяти, где этот объект располагается.

Рассмотрим ошибочный алгоритм, который часто используют новички:

  1. Создаем объект Запись = Новый Структура до начала цикла.
  2. Входим в цикл.
  3. Заполняем свойства этой структуры Запись.Вставить(...).
  4. Добавляем структуру в массив: История.Добавить(Запись).

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

Решение №1: Переинициализация объекта внутри цикла

Выясним причину неэффективности метода Очистить(). В обсуждении предлагалось использовать Запись.Очистить() перед заполнением. Это не поможет, так как метод просто удаляет ключи из существующего объекта, но сам адрес объекта в памяти остается неизменным. Все элементы массива по-прежнему будут ссылаться на этот обновляемый адрес.

Единственный верный способ — это создание нового экземпляра объекта на каждой итерации цикла. Рассмотрим исправленный программный код:


История = Новый Массив;

Для каждого СтрРасх Из Объект.Расходы Цикл
    Если Строка(СтрРасх.СтатьяРасходаИмущество) = "Бензин АИ-92" Тогда
        
        // КЛЮЧЕВОЙ МОМЕНТ: Создаем новый объект в памяти
        Запись = Новый Структура; 
        
        Запись.Вставить("Дата", Объект.Дата);
        Запись.Вставить("Имущество", СтрРасх.РасходыРеквизитАналитики_36);
        Запись.Вставить("Статья", СтрРасх.СтатьяРасходаИмущество);
        Запись.Вставить("Пробег", СтрРасх.РасходыРеквизитАналитики_71);
        Запись.Вставить("Цена", СтрРасх.Цена);
        Запись.Вставить("Количество", СтрРасх.Количество);
        
        // Теперь в массив добавляется ссылка на НОВЫЙ участок памяти
        История.Добавить(Запись);
        
    КонецЕсли;
КонецЦикла;

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

Проблема скрытых вызовов сервера

Проанализируем ситуацию с производительностью, затронутую в обсуждении. Использование конструкции Строка(СтрРасх.СтатьяРасходаИмущество) внутри цикла на стороне клиента — это серьезный «антипаттерн» в разработке 1С.

Если СтатьяРасходаИмущество является ссылкой на справочник, то при попытке преобразовать её в строку на клиенте, платформа вынуждена неявно обратиться к серверу (и к базе данных), чтобы получить строковое представление этого объекта. Если в таблице 100 строк, ваша программа сделает 100 микро-запросов к серверу. На слабом канале связи это вызовет значительные задержки («фризы») интерфейса.

Рекомендация: Все манипуляции с поиском и обработкой строк данных следует переносить на сервер. В идеале — передать таблицу на сервер один раз, обработать её и вернуть массив структур обратно на клиент.

Оптимизация: сравнение по ссылкам вместо строк

Сравнение Строка(...) = "Бензин АИ-92" крайне ненадежно. Посмотрим на причины:

Правильнее будет сравнивать ссылку со ссылкой. Для этого можно использовать предопределенные элементы или заранее найти нужную ссылку на сервере и передать её в качестве параметра.

Запись полученных данных в Регистр Сведений

Когда массив структур успешно сформирован, следующим шагом будет его запись в базу данных. Поскольку автор планирует записывать данные в Регистр Сведений, разберем два подхода:

  1. МенеджерЗаписи: Удобен для работы с одной конкретной записью. Если использовать его в цикле для каждой структуры из массива, это породит множество транзакций и нагрузит систему.
  2. НаборЗаписей: Это более профессиональный и быстрый способ. Рассмотрим по шагам, как это работает:
    • Создается объект РегистрыСведений.ВашРегистр.СоздатьНаборЗаписей().
    • Устанавливаются фильтры (Отбор), чтобы не затереть лишние данные.
    • Массив структур выгружается в таблицу набора.
    • Вызывается метод Записать(), который одним пакетом обновляет данные в БД.

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

Резюме

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

← На главную