Почему при добавлении метода в расширение недоступны варианты «Вызвать перед» и «Вызвать после»?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

Причина ограничения платформы

Выясним причину такого поведения. Платформа «1С:Предприятие» строго разграничивает применение директив перехвата для процедур и функций:

  1. Процедуры: Не возвращают значений в точку вызова. Платформа может последовательно выполнить цепочку методов с директивами &Перед, затем исходную процедуру конфигурации, а после нее — методы с директивой &После из одного или нескольких расширений.
  2. Функции: Обязаны возвращать результат работы через оператор Возврат. Если бы платформа допускала применение &Перед и &После для функций, возникла бы архитектурная неоднозначность: какое именно значение считать итоговым результатом функции, если каждое расширение попытается его изменить, и в каком порядке эти значения должны перезаписываться?

Именно поэтому для любых функций перехват через аннотации &Перед и &После на уровне платформы принципиально запрещен. Доступны только методы полной замены: &Вместо и &ИзменениеИКонтроль.

Решение: симуляция «Перед» и «После» через директиву «Вместо»

Проанализируем практическое решение. Если нам требуется дополнить логику типовой функции (выполнить предварительные действия, скорректировать параметры или обработать результат перед возвратом), мы можем использовать аннотацию &Вместо в связке с системной функцией ПродолжитьВызов().

Посмотрим на пример перехвата экспортной функции формирования печатной формы:


&Вместо("ПечатьТ7")
Функция _ПечатьТ7(МассивОбъектов, ОбъектыПечати)
    
    // Блок действий "Перед"
    // Здесь можно проверить или модифицировать входные параметры
    Если МассивОбъектов.Количество() = 0 Тогда
        ВызватьИсключение "Не выбраны объекты для печати!";
    КонецЕсли;
    
    // Вызов оригинальной функции конфигурации (или следующего расширения)
    Результат = ПродолжитьВызов(МассивОбъектов, ОбъектыПечати);
    
    // Блок действий "После"
    // Здесь мы можем проанализировать или изменить возвращенное значение
    Если Результат <> Неопределено Тогда
        // Выполняем постобработку результата, например, модификацию табличного документа
    КонецЕсли;
    
    // Обязательно возвращаем результат наружу
    Возврат Результат;

КонецФункции

Разберем по шагам, как работает данная конструкция:

  1. До вызова ПродолжитьВызов() мы выполняем любой код, который требовалось поместить в «Перед». При необходимости можно изменить переданные параметры (если они передаются по ссылке).
  2. Вызов ПродолжитьВызов() передает управление исходному телу функции в основной конфигурации.
  3. Значение, которое вернула базовая функция, сохраняется в переменную (в нашем примере — Результат).
  4. После выполнения ПродолжитьВызов() пишется код, который планировался для «После». Мы можем прочитать или модифицировать сформированный объект.
  5. В конце функции обязательно вызывается оператор Возврат Результат.

Важные нюансы и риски при работе с «&Вместо»

При использовании директивы &Вместо следует помнить о нескольких критически важных моментах:

  1. Явный возврат значения: Если забыть оператор Возврат или вызвать его без переменной, функция неявно вернет значение Неопределено, что приведет к трудноуловимым ошибкам в коде конфигурации, ожидающем конкретный тип данных.
  2. Передача параметров: Параметры в метод ПродолжитьВызов() должны передаваться в точном соответствии с сигнатурой оригинального метода — с соблюдением порядка и количества аргументов.
  3. Порядок применения расширений: Если один и тот же метод перехвачен с директивой &Вместо в нескольких расширениях, платформа выполнит метод только из расширения с наибольшим приоритетом. Если внутри него не вызван ПродолжитьВызов(), исполнение кода остальных расширений и оригинальной функции будет заблокировано.

Альтернативный вариант: директива «&ИзменениеИКонтроль»

Если логика функции сложная, и простой обертки через ПродолжитьВызов() недостаточно, рассмотрим альтернативный инструмент — «Вызвать вместо (с контролем)» (директива &ИзменениеИКонтроль).

В этом режиме тело оригинальной функции полностью копируется в расширение, а доработки вносятся с помощью специальных тегов:


&ИзменениеИКонтроль("СформироватьДанные")
Функция СформироватьДанные(Параметры)
    
    Результат = Новый Структура;
    
#Вставка
    // Наш дополнительный код
    Результат.Вставить("ДополнительноеПоле", Истина);
#КонецВставки

    Возврат Результат;
    
КонецФункции

Преимущество директивы &ИзменениеИКонтроль заключается в автоматическом контроле применимости: если при обновлении типовой конфигурации разработчики изменят исходный код функции, платформа выдаст предупреждение о несовпадении, предотвратив скрытые сбои логики. Однако следует учитывать ограничение: перехватить один метод через &ИзменениеИКонтроль может только одно расширение.

Дополнительные ограничения при перехвате методов в расширениях

Проанализируем другие правила платформы, которые важно учитывать при заимствовании процедур и функций:

  1. Только экспортные методы: В общих модулях через расширение можно переопределять только те процедуры и функции, которые объявлены с ключевым словом Экспорт. Локальные методы модулей перехватить нельзя.
  2. Глобальные модули: Платформа запрещает перехват методов в глобальных общих модулях.
  3. Контекст привилегированного режима: Перехваченные методы расширения по умолчанию не наследуют привилегированный режим исходного общего модуля. Если для выполнения операций требуются повышенные права, необходимо явно устанавливать их через УстановитьПривилегированныйРежим(Истина).
← На главную