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