В процессе разработки на платформе 1С:Предприятие 8.3 (Управляемые формы) программисты часто сталкиваются с необходимостью вынести логику заполнения реквизитов в общие модули. Казалось бы, задача проста: передать реквизит как параметр и присвоить ему новое значение. Однако, если реквизит имеет примитивный тип (например, Строка или Число), стандартная передача «по ссылке» может не сработать так, как ожидается, особенно при использовании асинхронных вызовов. Рассмотрим подробно, почему это происходит и как правильно организовать архитектуру кода для решения этой задачи.
Разберем ситуацию, когда у нас есть реквизит обработки ИмяФайла с типом Строка. Мы вызываем процедуру общего модуля, чтобы она нашла файл и записала путь в этот реквизит. В языке 1С параметры процедур могут передаваться двумя способами: по значению (с использованием ключевого слова Знач) и по ссылке (без него). Если мы передаем переменную в пределах одной процедуры или в синхронный вызов, изменение параметра внутри процедуры изменит и исходную переменную.
Но ситуация в корне меняется, когда в цепочке вызовов появляется асинхронность (например, выбор файла, вопрос пользователю или открытие формы). Когда мы помещаем строку в Структура для передачи в ОписаниеОповещения, связь с исходным реквизитом формы разрывается. В структуру копируется само значение строки, а не ссылка на ячейку памяти или реквизит объекта. Проанализируем это на примерах и найдем рабочие способы решения.
Если логика в общем модуле не требует ожидания действий пользователя (синхронная работа), самым простым и надежным способом будет использование функции. Вместо того чтобы пытаться «пропихнуть» значение внутрь процедуры, мы просто получаем результат и присваиваем его реквизиту на стороне формы.
Рассмотрим пример кода на форме:
&НаКлиенте
Процедура ЗаполнитьРеквизитСинхронно()
// Вместо передачи реквизита параметром, получаем значение из функции
Объект.ИмяФайла = ОбщМодуль_Клиент.НайтиПутьКФайлу(Объект.ПараметрПоиска);
КонецПроцедуры
Этот метод исключает любые путаницы с передачей ссылок и является максимально прозрачным для отладки.
Если нам необходимо изменить реквизит именно внутри процедуры общего модуля (например, процедура универсальная и должна заполнять разные поля) или если используется асинхронный вызов через ОписаниеОповещения, мы должны использовать динамическое обращение к свойствам. Вместо самой строки мы передаем «владельца» этой строки (объект ДанныеФормыСтруктура) и имя поля в виде текста.
Рассмотрим, как это реализовать по шагам:
Объект (который всегда передается по ссылке как сложный тип данных) и строку с именем реквизита.Пример реализации в общем модуле:
// Вызывающая процедура в общем модуле
Процедура ПодобратьФайл(КонтейнерДанных, ИмяРеквизита) Экспорт
ДопПараметры = Новый Структура;
ДопПараметры.Вставить("Объект", КонтейнерДанных);
ДопПараметры.Вставить("ИмяПоля", ИмяРеквизита);
Оповещение = Новый ОписаниеОповещения("ВыборФайлаЗавершение", ЭтотОбъект, ДопПараметры);
Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
Диалог.Показать(Оповещение);
КонецПроцедуры
// Процедура обратного вызова
Процедура ВыборФайлаЗавершение(ВыбранныеФайлы, ДополнительныеПараметры) Экспорт
Если ВыбранныеФайлы <> Неопределено Тогда
// Используем динамическое обращение к свойству объекта
ОбъектВладелец = ДополнительныеПараметры.Объект;
ИмяСвойства = ДополнительныеПараметры.ИмяПоля;
ОбъектВладелец[ИмяСвойства] = ВыбранныеФайлы[0];
КонецЕсли;
КонецПроцедуры
Почему это работает? Объект Объект на форме имеет тип ДанныеФормыСтруктура. В 1С объекты этого типа позволяют обращаться к своим свойствам не только через точку (Объект.ИмяФайла), но и по индексу (Объект["ИмяФайла"]). Поскольку сам объект в структуру передается как ссылка, изменения в нем сразу отображаются в интерфейсе формы.
Если вы работаете на платформе версии 8.3.18 и выше, мы настоятельно рекомендуем отказаться от ОписаниеОповещения в пользу механизмов Асинх и Ждать. Это позволяет писать асинхронный код в линейном стиле, сохраняя область видимости локальных переменных и прямой доступ к реквизитам формы.
Проанализируем пример современного подхода:
&НаКлиенте
Асинх Процедура КомандаЗаполнитьФайлАсинхронно(Команда)
// Вызываем асинхронную функцию подбора
ПутьКФайлу = Ждать ПолучитьПутьКФайлуАсинхронно();
Если ПутьКФайлу <> Неопределено Тогда
// Прямое присваивание в контексте формы
Объект.ИмяФайла = ПутьКФайлу;
КонецЕсли;
КонецПроцедуры
&НаКлиенте
Асинх Функция ПолучитьПутьКФайлуАсинхронно()
Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.Открытие);
Результат = Ждать Диалог.ВыбратьАсинх();
Если Результат <> Неопределено Тогда
Возврат Результат[0];
КонецЕсли;
Возврат Неопределено;
КонецФункции
При таком подходе проблема «передачи по ссылке» исчезает сама собой, так как код присваивания выполняется непосредственно в контексте формы после того, как асинхронная функция вернет результат.
При работе с реквизитами и параметрами следует учитывать еще несколько важных моментов:
Строка, Число, Булево, Дата) всегда копируются при помещении в коллекции (Структура, Массив, Соответствие). Ссылочные типы (СправочникСсылка, ДокументСсылка) тоже копируются, но сама ссылка все еще указывает на тот же объект в базе данных. Однако изменение ссылки в структуре не изменит ссылку в исходном реквизите формы — это важно помнить!Объект — это основной реквизит, связанный с данными документа или обработки. Если вам нужно передать реквизит, который создан просто как «Реквизит формы» (не входящий в состав Объект), передавайте в процедуру общего модуля переменную ЭтаФорма. К ней также можно обращаться через квадратные скобки: ЭтаФорма["ИмяРеквизитаФормы"] = Значение;.Процедура Моя(Знач Параметр), то никакое изменение внутри не повлияет на внешнюю переменную, даже если это синхронный вызов.Выяснив причину разрыва связи между переменной и ее значением в асинхронных вызовах, мы можем сделать вывод: для надежной работы в 1С 8.3 следует либо использовать возвращаемые значения функций, либо передавать контейнер (Объект/Форму) вместе со строковым именем нужного реквизита.