Как в расширении 1С прервать выполнение процедуры основной конфигурации, используя директиву &Перед?

Программист 1С v8.3 (Управляемые формы)
← К списку

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

1. Почему директива &Перед сама по себе не прерывает выполнение?

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

2. Решение 1: Прерывание цепочки обработчиков с помощью УстановитьВыполнениеОбработчиковСобытия(Ложь)

Хотя &Перед напрямую не прерывает выполнение, существует способ остановить дальнейшее выполнение цепочки обработчиков событий (включая метод основной конфигурации) после завершения вашего обработчика расширения. Для этого мы будем использовать метод глобального контекста УстановитьВыполнениеОбработчиковСобытия(). Разберем по шагам, как это работает: 1. Метод УстановитьВыполнениеОбработчиковСобытия(Ложь): Этот метод позволяет управлять потоком выполнения обработчиков событий. Если мы вызываем его с параметром Ложь, то после завершения текущего обработчика (вашей процедуры &Перед в расширении) платформа прекращает вызов всей оставшейся цепочки обработчиков для данного события. 2. Эффект: Таким образом, типовой метод основной конфигурации, который должен был быть вызван после всех обработчиков &Перед, не будет выполнен. Посмотрим на пример кода:


&Перед("МояПроцедура")
Процедура МояПроцедураПеред(Отказ, СтандартнаяОбработка)

    // Проверяем некоторое условие
    Если УсловиеДляПрерывания() Тогда
        Сообщить("Условие выполнено, прерываем выполнение основной процедуры.");

        // Останавливаем дальнейшее выполнение всех обработчиков события,
        // включая оригинальную процедуру конфигурации.
        УстановитьВыполнениеОбработчиковСобытия(Ложь);

        Возврат; // Завершаем выполнение нашего обработчика расширения

    КонецЕсли;

    // Если условие не выполнено, обработчик завершается,
    // и основная процедура будет выполнена.

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

Когда использовать это решение: Мы рекомендуем использовать УстановитьВыполнениеОбработчиковСобытия(Ложь), когда вам нужно добавить проверку или некоторую логику *до* выполнения типовой процедуры, и в случае выполнения определенного условия полностью предотвратить ее вызов, не изменяя при этом ее структуру или логику. Это наиболее корректный способ "прервать" типовой метод из &Перед.

3. Решение 2: Полный контроль с помощью директивы &Вместо и метода ПродолжитьВызов()

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


&Вместо("МояПроцедура")
Процедура МояПроцедураВместо()

    // Проверяем некоторое условие
    Если УсловиеДляВыполненияТиповогоКода() Тогда
        Сообщить("Условие выполнено, вызываем типовой код.");
        ПродолжитьВызов(); // Вызываем оригинальную процедуру основной конфигурации
    Иначе
        Сообщить("Типовой код не выполняется. Выполняем альтернативную логику.");
        // Здесь мы можем реализовать полностью свою логику
        // или просто ничего не делать, тем самым прервав выполнение типового кода.
    КонецЕсли;

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

Пример 2: Добавление логики до и после типового кода


&Вместо("МояПроцедура")
Процедура МояПроцедураВместо()

    // Логика, которая должна выполниться ДО типового кода
    Сообщить("Выполняем логику ДО типового кода.");
    
    // Вызываем оригинальную процедуру основной конфигурации
    ПродолжитьВызов();
    
    // Логика, которая должна выполниться ПОСЛЕ типового кода
    Сообщить("Выполняем логику ПОСЛЕ типового кода.");

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

Когда использовать это решение: Мы настоятельно рекомендуем использовать &Вместо в сочетании с ПродолжитьВызов(), когда вам требуется полный контроль над выполнением типового метода: * Полностью заменить типовую логику своей. * Условно выполнять типовую логику. * Добавить код до и/или после типовой логики в рамках одного обработчика. * Перехватывать функции, так как для них &Перед и &После не применимы.

4. Альтернативные подходы и почему их стоит избегать

На форуме также обсуждались другие методы, которые, хотя и могут дать желаемый эффект прерывания, имеют свои недостатки.

4.1. Использование ВызватьИсключение "Сообщение!"

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

4.2. Директива &ИзменениеИКонтроль

Директива &ИзменениеИКонтроль (доступная с платформы 8.3.15) позволяет вносить точечные изменения непосредственно в код типовой конфигурации с помощью директив препроцессора #Вставка, #КонецВставки, #Удаление и #КонецУдаления. Теоретически, вы могли бы вставить Возврат или другую логику. Почему стоит избегать: Платформа контролирует неизменность кода оригинальной процедуры за пределами этих вставок. Если типовой код изменяется вне ваших блоков #Вставка/#Удаление при обновлении конфигурации, это потребует ручного обновления вашего расширения. Это подтверждает опасения, высказанные на форуме, о потенциальных сложностях и трудозатратах при частых обновлениях от поставщика, что делает этот метод нежелательным для долгосрочной поддержки.

Общие рекомендации и выводы

Подведем итоги и сформулируем основные рекомендации, которые помогут вам принимать правильные решения при работе с расширениями: 1. Для дополнения логики без изменения основного поведения: Используйте &Перед или &После. Если вам нужно предотвратить выполнение типовой процедуры после &Перед, применяйте метод УстановитьВыполнениеОбработчиковСобытия(Ложь). 2. Для полного замещения или условного выполнения типовой логики: Мы настоятельно рекомендуем использовать &Вместо совместно с ПродолжитьВызов(). Этот подход обеспечивает наибольшую гибкость, прозрачность и контроль над потоком выполнения. 3. Для функций: Если вам нужно перехватить функцию, можно использовать только &Вместо. Метод ПродолжитьВызов() позволит вызвать исходную функцию внутри вашего обработчика расширения и получить ее результат. 4. Порядок выполнения нескольких расширений: Помните, что если несколько расширений перехватывают один и тот же метод, порядок их выполнения зависит от типа аннотации и порядка регистрации расширений. Для &Перед расширения выполняются в обратном порядке регистрации, а для &После — в прямом. 5. Обработка исключений: Необработанное исключение в любом из расширений или в расширяемой конфигурации прерывает всю цепочку вызовов методов. Используйте исключения только для обработки ошибок. Мы надеемся, что этот подробный разбор поможет вам эффективно управлять выполнением типовых процедур в ваших расширениях 1С!

← К списку