При разработке на платформе 1С:Предприятие программисты часто сталкиваются с ситуацией, когда несколько переменных неожиданно начинают изменять свои значения одновременно, хотя код обращается только к одной из них. Особенно ярко это проявляется при работе с коллекциями, такими как Структура, Массив или ТаблицаЗначений. Рассмотрим подробнее, почему так происходит, и разберем эффективные способы решения этой задачи.
Для начала проанализируем ситуацию, описанную в теме. Когда мы создаем объект ИтогиШаблон, система выделяет в оперативной памяти определенный участок, где хранятся ключи и значения этой структуры. Переменная ИтогиШаблон содержит не сами данные, а адрес (ссылку) на этот участок памяти.
Разберем по шагам, что происходит в коде автора:
ИтогиШаблон = Новый Структура("КоличествоППС, Итого", 0, 0);
// В памяти создан Объект_1. Переменная ИтогиШаблон указывает на Объект_1.
ИтогиПодраздедения = Новый Структура("Общ, ОМР", ИтогиШаблон, ИтогиШаблон);
// Теперь ИтогиПодразделения.Общ указывает на Объект_1.
// И ИтогиПодразделения.ОМР тоже указывает на Объект_1.
В результате, когда мы пишем ИтогиПодразделения.Общ.КоличествоППС = 1, мы обращаемся к Объект_1 и меняем в нем значение. Поскольку свойство ОМР ссылается на тот же самый Объект_1, при чтении ИтогиПодразделения.ОМР.КоличествоППС мы увидим ту же самую единицу. Это фундаментальное поведение объектов в 1С: они не копируются при присваивании, копируется только ссылка на них.
Важно помнить: строки, числа и булевы значения в 1С ведут себя иначе — они копируются по значению. А вот все универсальные коллекции (массивы, структуры, соответствия) всегда передаются по ссылке.
Самый простой, но «громоздкий» способ, к которому пришел автор — это создание каждого экземпляра структуры заново. Это гарантирует, что для каждого свойства будет выделен свой независимый участок памяти.
ИтогиПодраздедения = Новый Структура;
ИтогиПодраздедения.Вставить("Общ", Новый Структура("КоличествоППС, Итого", 0, 0));
ИтогиПодраздедения.Вставить("ОМР", Новый Структура("КоличествоППС, Итого", 0, 0));
Хотя этот метод работает, он неудобен, если структура шаблона большая или часто меняется. Рассмотрим более элегантные способы.
Мы можем написать универсальную функцию, которая будет создавать «клон» структуры, перебирая её элементы. Посмотрим на пример такой функции:
Функция СкопироватьСтруктуру(ИсходнаяСтруктура)
НоваяСтруктура = Новый Структура;
Для Каждого КлючЗначение Из ИсходнаяСтруктура Цикл
НоваяСтруктура.Вставить(КлючЗначение.Ключ, КлючЗначение.Значение);
КонецЦикла;
Возврат НоваяСтруктура;
КонецФункции
Однако здесь кроется ловушка. Этот метод выполняет поверхностное копирование. Если внутри вашей структуры ИтогиШаблон будет находиться другой массив или структура, то в новую копию опять попадет только ссылка на этот внутренний объект. Если вам нужна полная независимость объектов на всех уровнях вложенности, потребуется «глубокое копирование».
Один из самых современных и изящных способов сделать полную копию любого объекта — преобразовать его в строку и прочитать обратно. Это разрывает все связи с исходным объектом. Проанализируем, как это реализовать через JSON:
Функция ПолнаяКопияОбъекта(Объект)
ЗаписьJSON = Новый ЗаписьJSON;
ЗаписьJSON.УстановитьСтроку();
ЗаписатьJSON(ЗаписьJSON, Объект);
СтрокаJSON = ЗаписьJSON.Закрыть();
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(СтрокаJSON);
Возврат ПрочитатьJSON(ЧтениеJSON);
КонецФункции
Этот метод создаст абсолютно новый объект Структура со всеми вложенными данными. Преимущество: работает быстро и не требует написания рекурсивных циклов. Ограничение: JSON поддерживает не все типы данных 1С (например, он не перенесет напрямую ссылки на справочники без дополнительных настроек, но для структур с числами и строками подходит идеально).
Классический метод «старой школы», который гарантирует глубокое копирование любых типов данных, поддерживающих сериализацию в 1С (включая ссылки на документы и справочники):
Функция КлонироватьЧерезХранилище(Объект)
Возврат ЗначениеИзХранилища(Новый ХранилищеЗначения(Объект));
КонецФункции
При помещении в ХранилищеЗначения объект преобразуется в двоичные данные, а при извлечении — создается заново. Это надежный способ «отвязать» структуру от её шаблона.
Если ваша задача — использовать структуру только как неизменяемый эталон (readonly), вы можете воспользоваться объектом ФиксированнаяСтруктура. Это защитит данные от случайного изменения:
Шаблон = Новый Структура("Показатель", 0);
ФиксированныйШаблон = Новый ФиксированнаяСтруктура(Шаблон);
// Теперь ФиксированныйШаблон нельзя изменить, можно только читать.
Это хорошая практика для конфигурационных данных, которые передаются между модулями.
Подведем итог нашему анализу:
Новый Структура внутри цикла или при инициализации.Для каждого.Понимание механизмов работы с памятью в 1С позволяет избегать трудноуловимых ошибок, когда данные «магическим образом» меняются в разных частях программы одновременно. Использование предложенных функций поможет сделать ваш код более чистым и предсказуемым.