При настройке и выполнении синхронизации данных между конфигурациями «1С:Управление торговлей» (УТ) и «1С:Бухгалтерия предприятия» (БП) разработчики часто сталкиваются со специфическими ошибками обмена. Одна из самых распространенных и труднодиагностируемых проблем — ошибка вида: «Попытка передачи мутабельного значения результата метода...».
В этой статье мы подробно разберем, почему возникает данная ошибка, проанализируем механизмы работы платформы «1С:Предприятие 8» с мутабельными типами данных, выясним, почему поведение системы различается при запуске обмена из разных баз, и рассмотрим практические способы решения этой проблемы с примерами кода.
Для начала выясним природу термина «мутабельность» в контексте платформы 1С. Мутабельные (изменяемые) типы данных — это объекты, состояние и свойства которых могут быть изменены после их создания без генерации нового объекта в памяти. К таким типам относятся, например, СправочникОбъект, ДокументОбъект, ТаблицаЗначений, СписокЗначений и другие.
Ошибка передачи мутабельного значения возникает в двух основных случаях:
СписокЗначений, физически не могут быть сериализованы для передачи во внешнее приложение.В рассматриваемом нами случае вызов происходит следующим образом:
СписокОрганизаций = ОбщегоНазначенияБПВызовСервераПовтИсп.ПолучитьСписокОбособленныхПодразделений(Реквизиты.Организация);
Здесь задействован общий модуль с установленным свойством «Повторное использование возвращаемых значений» (ПовтИсп). По стандартам разработки фирмы «1С» возвращать мутабельные значения из таких модулей категорически запрещено. Если функция кэшируемого модуля вернет ссылку на мутабельный объект (например, СписокЗначений), то при повторном вызове вызывающий код получит не копию, а прямую ссылку на этот же объект из кэша. Если этот объект будет изменен в коде, кэш повредится для всего текущего сеанса работы, что приведет к непредсказуемому поведению программы.
Разберем важный нюанс: почему при запуске синхронизации из БП всё работает корректно, а при старте процесса из УТ возникает аварийное завершение?
СписокЗначений для передачи через COM-границу, но сталкивается с системным ограничением (сложная структура списка, визуальные свойства элементов, RLS-ограничения ссылок) и генерирует ошибку мутабельности.Проанализируем исходный код проблемного метода, который возвращает некорректный тип данных:
Функция ПолучитьСписокОбособленныхПодразделений(Организация) Экспорт
Запрос = Новый Запрос;
Запрос.УстановитьПараметр("Организация", Организация);
Запрос.Текст =
"ВЫБРАТЬ РАЗРЕШЕННЫЕ
| Организации.ГоловнаяОрганизация КАК ГоловнаяОрганизация
|ПОМЕСТИТЬ ТаблицаГоловнойОрганизации
|ИЗ
| Справочник.Организации КАК Организации
|ГДЕ
| Организации.Ссылка = &Организация
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| Организации.Ссылка,
| Организации.Наименование КАК Наименование
|ИЗ
| Справочник.Организации КАК Организации
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ ТаблицаГоловнойОрганизации КАК ТаблицаГоловнойОрганизации
| ПО Организации.ГоловнаяОрганизация = ТаблицаГоловнойОрганизации.ГоловнаяОрганизация
|УПОРЯДОЧИТЬ ПО
| Наименование";
СписокОП = Новый СписокЗначений;
Выборка = Запрос.Выполнить.Выбрать;
Пока Выборка.Следующий Цикл
СписокОП.Добавить(Выборка.Ссылка, Выборка.Наименование);
КонецЦикла;
Возврат СписокОП; // Ошибка! Возвращается мутабельный СписокЗначений
КонецФункции
Попытка разработчика добавить ключевое слово Знач в определение параметров (Знач Организация) не приносит результата, так как это влияет только на передачу входящего аргумента, но не меняет тип возвращаемого объекта СписокОП.
Для исправления ошибки нам необходимо отказаться от возврата типа СписокЗначений в пользу универсального, легко сериализуемого и безопасного типа Массив. Массив ссылок без проблем проходит сериализацию, легко передается через COM-соединение и кэшируется в повторно используемых модулях.
Рассмотрим исправленный вариант функции:
Функция ПолучитьСписокОбособленныхПодразделений(Организация) Экспорт
Запрос = Новый Запрос;
Запрос.УстановитьПараметр("Организация", Организация);
Запрос.Текст =
"ВЫБРАТЬ РАЗРЕШЕННЫЕ
| Организации.ГоловнаяОрганизация КАК ГоловнаяОрганизация
|ПОМЕСТИТЬ ТаблицаГоловнойОрганизации
|ИЗ
| Справочник.Организации КАК Организации
|ГДЕ
| Организации.Ссылка = &Организация
|;
|
|////////////////////////////////////////////////////////////////////////////////
|ВЫБРАТЬ
| Организации.Ссылка
|ИЗ
| Справочник.Организации КАК Организации
| ВНУТРЕННЕЕ СОЕДИНЕНИЕ ТаблицаГоловнойОрганизации КАК ТаблицаГоловнойОрганизации
| ПО Организации.ГоловнаяОрганизация = ТаблицаГоловнойОрганизации.ГоловнаяОрганизация";
МассивОП = Новый Массив;
Выборка = Запрос.Выполнить.Выбрать;
Пока Выборка.Следующий Цикл
МассивОП.Добавить(Выборка.Ссылка);
КонецЦикла;
Возврат МассивОП; // Правильно! Возвращаем простой массив ссылок
КонецФункции
Если мы хотим полностью следовать стандартам разработки 1С для модулей повторного использования, то возвращаемый массив нужно сделать неизменяемым. Для этого идеально подходит тип ФиксированныйМассив (FixedArray). Это гарантирует, что кэш платформы не будет случайно модифицирован вызывающим кодом.
Пример оптимизации возвращаемого значения:
// Вместо возврата обычного массива:
// Возврат МассивОП;
// Возвращаем фиксированный (защищенный) массив:
Возврат Новый ФиксированныйМассив(МассивОП);
Если в месте вызова этой функции бизнес-логика или интерфейсная форма обязательно требуют именно СписокЗначений (например, для интерактивного выбора пользователем или для установки отбора в динамическом списке), конвертацию данных необходимо выполнять на стороне вызывающего кода, уже после получения данных из кэшируемого модуля.
Разберем, как правильно переписать вызывающий код:
// 1. Получаем безопасный массив из повторно используемого общего модуля
МассивОрганизаций = ОбщегоНазначенияБПВызовСервераПовтИсп.ПолучитьСписокОбособленныхПодразделений(Реквизиты.Организация);
// 2. Создаем СписокЗначений локально в контексте формы/процедуры
СписокОрганизаций = Новый СписокЗначений;
// 3. Заполняем список полученными значениями из массива
СписокОрганизаций.ЗагрузитьЗначения(МассивОрганизаций);
Используя метод ЗагрузитьЗначения(), мы быстро и без лишних обходов циклом инициализируем локальный СписокЗначений. При таком подходе полностью исключаются ошибки мутабельности при обмене через COM/веб-сервисы, а стандарты безопасного программирования в 1С будут целиком соблюдены.