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