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

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

Как скопировать список значений в 1С

Как скопировать список значений в 1С: методы Скопировать() и СкопироватьЗначения(), копирование в другой список, таблицу значений и на форме. Примеры кода и ошибки.

Тип «СписокЗначений» в 1С — один из самых используемых объектов: в нём хранятся отборы, параметры запросов, списки документов для обработки, варианты выбора в полях формы. Задача скопировать такой список возникает постоянно — и почти всегда разработчик сталкивается с подвохом: простое присваивание переменной не копирует список, а создаёт вторую ссылку на тот же объект. Изменение «копии» меняет и оригинал, что приводит к трудноуловимым ошибкам в обработках и отчётах.

Способов корректного копирования несколько: встроенный метод Скопировать(), универсальная функция ЗначениеВСтрокуВнутр для глубокого копирования, поэлементный перенос в цикле, а также копирование между списком значений и таблицей значений или деревом. У каждого варианта есть свои условия применения, ограничения и типичные ошибки — особенно на управляемых формах, где список хранится как реквизит формы.

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

Почему простое присваивание не работает

Список значений в 1С — ссылочный объект. Когда вы пишете Список2 = Список1;, платформа не создаёт новый список: обе переменные начинают указывать на один и тот же объект в памяти. Внешне всё выглядит нормально — обе переменные содержат одинаковые элементы. Проблема проявляется позже: вы добавляете элемент в «копию», и он появляется в оригинале, удаляете строку из оригинала — она исчезает и в «копии».

Это особенно опасно в длинных цепочках вызовов: процедура получает список параметром, «копирует» его присваиванием, модифицирует под свои нужды — и вызывающий код внезапно получает испорченный исходный список. Такие ошибки сложно искать, потому что они проявляются не в месте изменения, а дальше по логике программы.

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

Основной способ: метод Скопировать()

У объекта «СписокЗначений» есть встроенный метод Скопировать(), который создаёт новый независимый список с тем же набором элементов. Это стандартный и рекомендуемый способ копирования в большинстве ситуаций.

Метод копирует не только сами значения, но и представления элементов, пометки (флажки) и картинки, если они заданы. То есть вы получаете полноценную копию, сохраняющую оформление исходного списка — это важно, когда список отображается пользователю с пометками.

Порядок действий
  1. 1
    Объявите переменную для нового списка: НовыйСписок = Неопределено или сразу присвойте результат
  2. 2
    Вызовите метод: НовыйСписок = ИсходныйСписок.Скопировать();
  3. 3
    Измените копию по своей задаче — добавьте или удалите элементы
  4. 4
    Проверьте, что исходный список не изменился: сравните Количество() у обоих списков

Пример кода:

ИсходныйСписок = Новый СписокЗначений;

ИсходныйСписок.Добавить("Значение1", "Первый элемент", Истина);

ИсходныйСписок.Добавить("Значение2", "Второй элемент", Ложь);

// Корректное копирование

КопияСписка = ИсходныйСписок.Скопировать();

// Меняем копию — оригинал не пострадает

КопияСписка.Добавить("Значение3", "Третий элемент");

КопияСписка.Удалить(0);

Сообщить(ИсходныйСписок.Количество()); // 2 — оригинал цел

Сообщить(КопияСписка.Количество()); // 2 — копия изменена

Копирование через цикл: когда нужен контроль над элементами

Если при копировании нужно отфильтровать элементы, преобразовать значения или перенести только помеченные строки, метод Скопировать() не подойдёт — он копирует всё. В этом случае создают пустой список и переносят элементы в цикле с нужными условиями.

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

НовыйСписок = Новый СписокЗначений;

Для Каждого Элемент Из ИсходныйСписок Цикл

// Копируем только помеченные элементы

Если Элемент.Пометка Тогда

НовыйСписок.Добавить(Элемент.Значение, Элемент.Представление, Элемент.Пометка);

КонецЕсли;

КонецЦикла;

Обратите внимание: при ручном переносе нужно явно передавать представление и пометку — если указать только значение, они потеряются, и визуально список на форме будет выглядеть иначе.

Типичные ошибки
01
Присваивание Список2 = Список1 вместо Скопировать() — обе переменные указывают на один объект, изменения копии портят оригинал
02
Перенос в цикле только значения без представления и пометки — на форме пропадают флажки и подписи элементов
03
Попытка скопировать список с реквизита формы напрямую в другой реквизит без промежуточной переменной — на управляемых формах это может вызвать ошибку сериализации
04
Ожидание, что Скопировать() сделает копии вложенных объектов — метод копирует ссылки на значения, а не сами значения, если они ссылочного типа

Поверхностное и глубокое копирование: важное различие

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

На практике это означает: измените вложенную структуру через копию списка — изменится она и в оригинале. Для большинства задач (списки ссылок, строк, чисел) это несущественно, но при работе со списками структур нужна осторожность.

Для полного глубокого копирования произвольных значений используют сериализацию через ЗначениеВСтрокуВнутр() и ЗначениеИзСтрокиВнутр():

// Глубокое копирование любого значения

Функция СкопироватьЗначениеГлубоко(Значение)

Возврат ЗначениеИзСтрокиВнутр(ЗначениеВСтрокуВнутр(Значение));

КонецФункции

КопияСписка = СкопироватьЗначениеГлубоко(ИсходныйСписок);

Копирование списка значений на управляемой форме

Когда список значений является реквизитом формы, копирование имеет особенности. Реквизит формы типа «СписокЗначений» ведёт себя как обычный список, и метод Скопировать() работает, но присваивать результат нужно аккуратно — особенно при передаче между клиентом и сервером.

Типичная задача — скопировать список из одного реквизита формы в другой. Безопасный вариант — выполнить копирование в серверной процедуре или через промежуточную переменную:

&НаКлиенте

Процедура СкопироватьСписок(Команда)

// Создаём новый список и заполняем из реквизита формы

НовыйСписок = Новый СписокЗначений;

Для Каждого Элемент Из СписокИсточник Цикл

НовыйСписок.Добавить(Элемент.Значение, Элемент.Представление, Элемент.Пометка);

КонецЦикла;

// Очищаем приёмник и заполняем заново

СписокПриемник.Очистить();

Для Каждого Элемент Из НовыйСписок Цикл

СписокПриемник.Добавить(Элемент.Значение, Элемент.Представление, Элемент.Пометка);

КонецЦикла;

КонецПроцедуры

Прямое присваивание СписокПриемник = СписокИсточник.Скопировать(); на клиенте обычно тоже работает, но при передаче данных между формами (например, возврат списка из формы выбора через Закрыть(Список)) надёжнее передавать новый список, созданный явно.

Копирование между списком значений и другими коллекциями

Часто список значений нужно перенести в таблицу значений (для дальнейшей обработки запросом) или наоборот — заполнить список из таблицы. Прямого метода копирования между разными типами коллекций нет, перенос делается в цикле.

Из списка значений в таблицу значений

Таблица = Новый ТаблицаЗначений;

Таблица.Колонки.Добавить("Значение");

Таблица.Колонки.Добавить("Представление");

Для Каждого Элемент Из ИсходныйСписок Цикл

НоваяСтрока = Таблица.Добавить();

НоваяСтрока.Значение = Элемент.Значение;

НоваяСтрока.Представление = Элемент.Представление;

КонецЦикла;

Из таблицы значений в список значений

Список = Новый СписокЗначений;

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

Список.Добавить(Строка.Ссылка, Строка.Наименование);

КонецЦикла;

Есть и обратный приём: метод ВыгрузитьЗначения() у таблицы значений (точнее, у колонки таблицы через Таблица.ВыгрузитьКолонку("ИмяКолонки")) возвращает массив, которым можно заполнить список через Список.ЗагрузитьЗначения(Массив). Это короткий способ перенести одну колонку в список без явного цикла:

Список.ЗагрузитьЗначения(Таблица.ВыгрузитьКолонку("Ссылка"));

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

Скопировать()

Стандартный способ полного копирования списка с пометками и представлениями. Не подходит, если нужна фильтрация или глубокое копирование вложенных объектов

Цикл с Добавить()

Полный контроль: фильтрация, преобразование, перенос между разными коллекциями. Требует больше кода

ЗначениеВСтрокуВнутр / ИзСтрокиВнутр

Глубокое копирование вместе со вложенными объектами. Работает только на сервере и в толстом клиенте

ЗагрузитьЗначения / ВыгрузитьЗначения

Быстрый обмен с массивом без цикла. Копируются только значения, представления и пометки теряются

Проверка результата копирования

После копирования полезно убедиться, что списки действительно независимы. Самый простой тест — добавить элемент в копию и сравнить количество элементов в обоих списках. Если количество изменилось в обоих — копирование выполнено неправильно, где-то осталось присваивание ссылки.

Для отладки удобно выводить содержимое обоих списков в сообщения или сравнивать их сериализованное представление. В конфигураторе можно проверить через табло (Shift+F9): вычислить Список1 = Список2 — если результат «Истина» для двух разных переменных, это один и тот же объект.

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

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

Добавили элемент в копию
Норма

Количество элементов в оригинале не изменилось

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

Элемент появился и в исходном списке — копирование не выполнено

Сравнение переменных в табло
Норма

Список1 = Список2 даёт Ложь — это разные объекты

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

Результат Истина — обе переменные ссылаются на один список

Пометки и представления в копии
Норма

Флажки и подписи элементов совпадают с оригиналом

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

Пометки сброшены — при переносе не передавались параметры Добавить()

FAQ

Как скопировать список значений в параметр запроса?

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

Как скопировать только помеченные элементы списка?

Пройдите по исходному списку в цикле и добавляйте в новый список только элементы, у которых Элемент.Пометка = Истина. Метод Скопировать() здесь не подходит — он копирует все элементы без фильтрации.

Можно ли скопировать список значений в массив и обратно?

Да. В массив: метод Список.ВыгрузитьЗначения() вернёт массив значений элементов. Обратно: Список.ЗагрузитьЗначения(Массив) заполнит список из массива. Учтите, что при этом переносятся только значения — представления и пометки не сохраняются.

Почему после копирования на форме пропали пометки у элементов?

Скорее всего, список был перенесён в цикле с вызовом Добавить(Элемент.Значение) без дополнительных параметров. Чтобы пометки и представления сохранились, передавайте их явно: Добавить(Элемент.Значение, Элемент.Представление, Элемент.Пометка), либо используйте метод Скопировать().

Работает ли Скопировать() в тонком клиенте?

Да, метод Скопировать() доступен во всех клиентах, включая тонкий и веб-клиент. Ограничения касаются только ЗначениеВСтрокуВнутр — этот способ глубокого копирования доступен только на сервере и в толстом клиенте.

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

Напрямую объекты между базами не передаются. Сериализуйте список через ЗначениеВСтрокуВнутр() в исходной базе, передайте строку (через файл, буфер обмена или веб-сервис) и восстановите через ЗначениеИзСтрокиВнутр() в приёмнике. Учтите, что ссылки на объекты другой базы будут битыми — переносите вместо ссылок идентификаторы или коды.

Итог

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

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