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

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

Мутабельное значение в 1С: что это такое и как с ним работать

Мутабельное значение в 1С: что это такое, какие типы изменяемы, как устроены ссылки и значения, типичные ошибки при передаче параметров и копировании данных.

Термин «мутабельное значение» пришёл в мир 1С из классического программирования, где все типы данных делятся на изменяемые (mutable) и неизменяемые (immutable). В языке Python, Java или C# это фундаментальное свойство типа. Во встроенном языке 1С такого формального деления нет, однако понятие активно используется разработчиками для описания реального поведения объектов: одни значения можно изменить «по ссылке», и изменение увидят все, кто держит эту ссылку, а другие при любой модификации фактически порождают новое значение.

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

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

Что такое мутабельность и почему в 1С о ней нужно знать

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

Во встроенном языке 1С нет ключевых слов вроде mutable или const, которые бы фиксировали это свойство. Тем не менее поведение типов платформы чётко разделяется на два лагеря. Составные объектные типы — таблицы значений, массивы, структуры, соответствия, списки значений, деревья значений — являются ссылочными и изменяемыми. Примитивные типы — строка, число, дата, булево, неопределено, NULL — передаются и копируются по значению и фактически неизменяемы.

Знать об этом нужно по трём практическим причинам. Во-первых, от этого зависит, увидит ли вызывающий код изменения, сделанные внутри процедуры. Во-вторых, от этого зависит корректность копирования данных: простое присваивание Копия = Исходный для мутабельного объекта не создаёт копию, а лишь добавляет ещё одну ссылку на тот же объект. В-третьих, это влияет на производительность: передача большой таблицы по ссылке и по значению — принципиально разные операции.

Какие типы в 1С мутабельны, а какие нет

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

ТипПоведениеЧто происходит при присваивании
Строка, Число, Дата, БулевоИммутабельны (по значению)Создаётся независимая копия значения
Массив, Структура, СоответствиеМутабельны (по ссылке)Копируется ссылка на тот же объект
ТаблицаЗначений, ДеревоЗначенийМутабельны (по ссылке)Копируется ссылка; для копии нужен метод Скопировать()
СписокЗначенийМутабелен (по ссылке)Копируется ссылка; для копии нужен Скопировать()
Ссылки на объекты БД (СправочникСсылка и т.п.)Сама ссылка иммутабельнаКопируется ссылка; объект в БД меняется через ПолучитьОбъект()

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

Структура и соответствие — частый источник путаницы. Добавление ключа в структуру внутри функции изменит ту самую структуру, которую передал вызывающий код, если параметр не объявлен с Знач. Это удобно для накопления результатов, но опасно, когда структура используется как неизменяемый набор настроек.

Как мутабельность проявляется при передаче параметров

Главное место, где разница между мутабельным и иммутабельным поведением «стреляет», — вызов процедур и функций. В 1С параметр по умолчанию передаётся по ссылке, а модификатор Знач заставляет платформу передать копию значения.

Для иммутабельных типов разницы почти нет: число или строку процедура не сможет изменить в вызывающем коде и без Знач — присваивание параметру нового значения лишь перенаправит локальную переменную. А вот для мутабельных типов разница принципиальна. Если процедура получает таблицу значений без Знач и вызывает Таблица.Очистить(), очистится та самая таблица, с которой работает вызывающий код.

Процедура ДобавитьСтроку(Таб, Знач Сумма)

НоваяСтрока = Таб.Добавить(); // изменит таблицу вызывающего кода

НоваяСтрока.Сумма = Сумма; // сама Сумма защищена модификатором Знач

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

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

Копирование мутабельных объектов: где прячутся ошибки

Самая распространённая ошибка новичков — попытка скопировать таблицу значений или массив простым присваиванием. Строка Таб2 = Таб1 не создаёт вторую таблицу: обе переменные теперь указывают на один объект. Удаление строки через Таб2 удалит её и в «оригинале».

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

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

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

НоваяСтруктура = Новый Структура;

Для Каждого КлючИЗначение Из ИсходнаяСтруктура Цикл

НоваяСтруктура.Вставить(КлючИЗначение.Ключ, КлючИЗначение.Значение);

КонецЦикла;

Важно понимать глубину копирования. Скопировать() у таблицы значений копирует строки и значения ячеек, но если в ячейке лежит другой мутабельный объект (например, вложенная таблица или массив), скопируется только ссылка на него. Такое поверхностное копирование — стандартное поведение, и для глубокой копии вложенных структур потребуется собственный рекурсивный код или сериализация, например через ЗначениеВСтрокуВнутр / ЗначениеИзСтрокиВнутр.

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

Мутабельность в типовых сценариях разработки

Накопление результата в структуре или массиве

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

Реквизиты формы и данные на клиенте

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

Кэширование и повторное использование коллекций

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

Быстрый способ проверить, ссылаются ли две переменные на один объект: измените объект через одну переменную (добавьте строку или ключ) и посмотрите в отладчике, изменилась ли вторая. Если да — ссылка общая, и простое присваивание где-то заменило копирование.
ПЗ
Практическое замечание

Производительность: когда передача по ссылке выгодна

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

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

Маршрут решения

Как выбрать способ передачи

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

Как проверить код на ошибки мутабельности

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

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

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

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

Поведение коллекции после вызова процедуры
Норма

Содержимое не изменилось, если процедура должна была только читать

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

Строки или ключи появились/исчезли без явного намерения

Копия после Скопировать()
Норма

Изменение копии не затрагивает оригинал

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

Обе переменные показывают одни и те же изменения — копии нет

Кэш, возвращаемый из функции
Норма

Каждый вызов возвращает независимый объект

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

Очистка результата одним потребителем ломает данные для остальных

Итог

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

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

Частые вопросы о мутабельных значениях в 1С

Чем мутабельное значение отличается от ссылки на объект базы данных?

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

Почему после «Копия = Таблица» удаление строки удаляет её в обеих переменных?

Потому что присваивание для таблицы значений копирует только ссылку, а не данные. Обе переменные указывают на один объект. Для независимой копии используйте метод Скопировать(), при необходимости с отбором строк и списком колонок.

Когда стоит использовать модификатор Знач у параметра?

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

Можно ли сделать структуру полностью неизменяемой?

Штатного «замораживания» структуры в платформе нет. Приём, близкий к неизменяемости, — фиксированная структура, созданная конструктором Новый Структура("Ключ1, Ключ2"): добавить новый ключ в неё нельзя, но значения существующих ключей менять можно. Полную защиту даёт только передача копии.

Как сделать глубокую копию структуры с вложенными массивами?

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

Влияет ли мутабельность на работу в фоновых заданиях?

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