Разработка и Конфигурация · 12 мин
Как скопировать список значений в 1С
Как скопировать список значений в 1С: методы Скопировать() и СкопироватьЗначения(), копирование в другой список, таблицу значений и на форме. Примеры кода и ошибки.
Тип «СписокЗначений» в 1С — один из самых используемых объектов: в нём хранятся отборы, параметры запросов, списки документов для обработки, варианты выбора в полях формы. Задача скопировать такой список возникает постоянно — и почти всегда разработчик сталкивается с подвохом: простое присваивание переменной не копирует список, а создаёт вторую ссылку на тот же объект. Изменение «копии» меняет и оригинал, что приводит к трудноуловимым ошибкам в обработках и отчётах.
Способов корректного копирования несколько: встроенный метод Скопировать(), универсальная функция ЗначениеВСтрокуВнутр для глубокого копирования, поэлементный перенос в цикле, а также копирование между списком значений и таблицей значений или деревом. У каждого варианта есть свои условия применения, ограничения и типичные ошибки — особенно на управляемых формах, где список хранится как реквизит формы.
Ниже разобраны все рабочие варианты с примерами кода, различие поверхностного и глубокого копирования, особенности копирования на форме и способы проверить, что копия действительно независима от оригинала.
Почему простое присваивание не работает
Список значений в 1С — ссылочный объект. Когда вы пишете Список2 = Список1;, платформа не создаёт новый список: обе переменные начинают указывать на один и тот же объект в памяти. Внешне всё выглядит нормально — обе переменные содержат одинаковые элементы. Проблема проявляется позже: вы добавляете элемент в «копию», и он появляется в оригинале, удаляете строку из оригинала — она исчезает и в «копии».
Это особенно опасно в длинных цепочках вызовов: процедура получает список параметром, «копирует» его присваиванием, модифицирует под свои нужды — и вызывающий код внезапно получает испорченный исходный список. Такие ошибки сложно искать, потому что они проявляются не в месте изменения, а дальше по логике программы.
Тот же принцип действует для таблиц значений, деревьев значений и других коллекций платформы. Правило простое: если нужна независимая копия коллекции — используйте специальный метод копирования, а не оператор присваивания.
Основной способ: метод Скопировать()
У объекта «СписокЗначений» есть встроенный метод Скопировать(), который создаёт новый независимый список с тем же набором элементов. Это стандартный и рекомендуемый способ копирования в большинстве ситуаций.
Метод копирует не только сами значения, но и представления элементов, пометки (флажки) и картинки, если они заданы. То есть вы получаете полноценную копию, сохраняющую оформление исходного списка — это важно, когда список отображается пользователю с пометками.
- 1Объявите переменную для нового списка: НовыйСписок = Неопределено или сразу присвойте результат
- 2Вызовите метод: НовыйСписок = ИсходныйСписок.Скопировать();
- 3Измените копию по своей задаче — добавьте или удалите элементы
- 4Проверьте, что исходный список не изменился: сравните Количество() у обоих списков
Пример кода:
ИсходныйСписок = Новый СписокЗначений;
ИсходныйСписок.Добавить("Значение1", "Первый элемент", Истина);
ИсходныйСписок.Добавить("Значение2", "Второй элемент", Ложь);
// Корректное копирование
КопияСписка = ИсходныйСписок.Скопировать();
// Меняем копию — оригинал не пострадает
КопияСписка.Добавить("Значение3", "Третий элемент");
КопияСписка.Удалить(0);
Сообщить(ИсходныйСписок.Количество()); // 2 — оригинал цел
Сообщить(КопияСписка.Количество()); // 2 — копия изменена
Копирование через цикл: когда нужен контроль над элементами
Если при копировании нужно отфильтровать элементы, преобразовать значения или перенести только помеченные строки, метод Скопировать() не подойдёт — он копирует всё. В этом случае создают пустой список и переносят элементы в цикле с нужными условиями.
Такой подход даёт полный контроль: можно копировать только помеченные элементы, исключить дубли, изменить представления или типы значений. По сути это уже не просто копирование, а копирование с трансформацией.
НовыйСписок = Новый СписокЗначений;
Для Каждого Элемент Из ИсходныйСписок Цикл
// Копируем только помеченные элементы
Если Элемент.Пометка Тогда
НовыйСписок.Добавить(Элемент.Значение, Элемент.Представление, Элемент.Пометка);
КонецЕсли;
КонецЦикла;
Обратите внимание: при ручном переносе нужно явно передавать представление и пометку — если указать только значение, они потеряются, и визуально список на форме будет выглядеть иначе.
Поверхностное и глубокое копирование: важное различие
Метод Скопировать() выполняет поверхностное копирование: создаётся новый список, но значения элементов копируются как есть. Если элементом списка является ссылка на справочник или документ — это не проблема, ссылки и должны быть общими. Но если элементом является другой изменяемый объект (например, вложенный список значений, структура или таблица), то в копии окажется ссылка на тот же вложенный объект.
На практике это означает: измените вложенную структуру через копию списка — изменится она и в оригинале. Для большинства задач (списки ссылок, строк, чисел) это несущественно, но при работе со списками структур нужна осторожность.
Для полного глубокого копирования произвольных значений используют сериализацию через ЗначениеВСтрокуВнутр() и ЗначениеИзСтрокиВнутр():
// Глубокое копирование любого значения
Функция СкопироватьЗначениеГлубоко(Значение)
Возврат ЗначениеИзСтрокиВнутр(ЗначениеВСтрокуВнутр(Значение));
КонецФункции
КопияСписка = СкопироватьЗначениеГлубоко(ИсходныйСписок);
Копирование списка значений на управляемой форме
Когда список значений является реквизитом формы, копирование имеет особенности. Реквизит формы типа «СписокЗначений» ведёт себя как обычный список, и метод Скопировать() работает, но присваивать результат нужно аккуратно — особенно при передаче между клиентом и сервером.
Типичная задача — скопировать список из одного реквизита формы в другой. Безопасный вариант — выполнить копирование в серверной процедуре или через промежуточную переменную:
&НаКлиенте
Процедура СкопироватьСписок(Команда)
// Создаём новый список и заполняем из реквизита формы
НовыйСписок = Новый СписокЗначений;
Для Каждого Элемент Из СписокИсточник Цикл
НовыйСписок.Добавить(Элемент.Значение, Элемент.Представление, Элемент.Пометка);
КонецЦикла;
// Очищаем приёмник и заполняем заново
СписокПриемник.Очистить();
Для Каждого Элемент Из НовыйСписок Цикл
СписокПриемник.Добавить(Элемент.Значение, Элемент.Представление, Элемент.Пометка);
КонецЦикла;
КонецПроцедуры
Прямое присваивание СписокПриемник = СписокИсточник.Скопировать(); на клиенте обычно тоже работает, но при передаче данных между формами (например, возврат списка из формы выбора через Закрыть(Список)) надёжнее передавать новый список, созданный явно.
Копирование между списком значений и другими коллекциями
Часто список значений нужно перенести в таблицу значений (для дальнейшей обработки запросом) или наоборот — заполнить список из таблицы. Прямого метода копирования между разными типами коллекций нет, перенос делается в цикле.
Из списка значений в таблицу значений
Таблица = Новый ТаблицаЗначений;
Таблица.Колонки.Добавить("Значение");
Таблица.Колонки.Добавить("Представление");
Для Каждого Элемент Из ИсходныйСписок Цикл
НоваяСтрока = Таблица.Добавить();
НоваяСтрока.Значение = Элемент.Значение;
НоваяСтрока.Представление = Элемент.Представление;
КонецЦикла;
Из таблицы значений в список значений
Список = Новый СписокЗначений;
Для Каждого Строка Из Таблица Цикл
Список.Добавить(Строка.Ссылка, Строка.Наименование);
КонецЦикла;
Есть и обратный приём: метод ВыгрузитьЗначения() у таблицы значений (точнее, у колонки таблицы через Таблица.ВыгрузитьКолонку("ИмяКолонки")) возвращает массив, которым можно заполнить список через Список.ЗагрузитьЗначения(Массив). Это короткий способ перенести одну колонку в список без явного цикла:
Список.ЗагрузитьЗначения(Таблица.ВыгрузитьКолонку("Ссылка"));
Сравнение вариантов
Стандартный способ полного копирования списка с пометками и представлениями. Не подходит, если нужна фильтрация или глубокое копирование вложенных объектов
Полный контроль: фильтрация, преобразование, перенос между разными коллекциями. Требует больше кода
Глубокое копирование вместе со вложенными объектами. Работает только на сервере и в толстом клиенте
Быстрый обмен с массивом без цикла. Копируются только значения, представления и пометки теряются
Проверка результата копирования
После копирования полезно убедиться, что списки действительно независимы. Самый простой тест — добавить элемент в копию и сравнить количество элементов в обоих списках. Если количество изменилось в обоих — копирование выполнено неправильно, где-то осталось присваивание ссылки.
Для отладки удобно выводить содержимое обоих списков в сообщения или сравнивать их сериализованное представление. В конфигураторе можно проверить через табло (Shift+F9): вычислить Список1 = Список2 — если результат «Истина» для двух разных переменных, это один и тот же объект.
Как проверить результат
Количество элементов в оригинале не изменилось
Элемент появился и в исходном списке — копирование не выполнено
Список1 = Список2 даёт Ложь — это разные объекты
Результат Истина — обе переменные ссылаются на один список
Флажки и подписи элементов совпадают с оригиналом
Пометки сброшены — при переносе не передавались параметры Добавить()
FAQ
Как скопировать список значений в параметр запроса?
Отдельно копировать не нужно: метод УстановитьПараметр() принимает список значений напрямую, и запрос работает с переданным списком, не изменяя его. Если тот же список используется в нескольких запросах с разной модификацией — сделайте копию через Скопировать() перед изменениями.
Как скопировать только помеченные элементы списка?
Пройдите по исходному списку в цикле и добавляйте в новый список только элементы, у которых Элемент.Пометка = Истина. Метод Скопировать() здесь не подходит — он копирует все элементы без фильтрации.
Можно ли скопировать список значений в массив и обратно?
Да. В массив: метод Список.ВыгрузитьЗначения() вернёт массив значений элементов. Обратно: Список.ЗагрузитьЗначения(Массив) заполнит список из массива. Учтите, что при этом переносятся только значения — представления и пометки не сохраняются.
Почему после копирования на форме пропали пометки у элементов?
Скорее всего, список был перенесён в цикле с вызовом Добавить(Элемент.Значение) без дополнительных параметров. Чтобы пометки и представления сохранились, передавайте их явно: Добавить(Элемент.Значение, Элемент.Представление, Элемент.Пометка), либо используйте метод Скопировать().
Работает ли Скопировать() в тонком клиенте?
Да, метод Скопировать() доступен во всех клиентах, включая тонкий и веб-клиент. Ограничения касаются только ЗначениеВСтрокуВнутр — этот способ глубокого копирования доступен только на сервере и в толстом клиенте.
Как скопировать список значений между двумя информационными базами?
Напрямую объекты между базами не передаются. Сериализуйте список через ЗначениеВСтрокуВнутр() в исходной базе, передайте строку (через файл, буфер обмена или веб-сервис) и восстановите через ЗначениеИзСтрокиВнутр() в приёмнике. Учтите, что ссылки на объекты другой базы будут битыми — переносите вместо ссылок идентификаторы или коды.
Что важно запомнить
Для копирования списка значений всегда используйте метод Скопировать() — присваивание создаёт лишь вторую ссылку на тот же объект. Для фильтрации при копировании применяйте цикл с Добавить(), для глубокого копирования вложенных объектов — сериализацию через ЗначениеВСтрокуВнутр на сервере. После копирования проверяйте независимость списков, добавив элемент в копию.