Почему параметр, переданный по значению в расширение через &После, меняет свой тип?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

При разработке расширений для типовых конфигураций 1С можно столкнуться с неожиданным поведением. Одна из таких ситуаций — когда параметр процедуры, объявленный с ключевым словом Знач (то есть передаваемый по значению), в обработчике расширения &После внезапно меняет свой тип. Например, вместо ожидаемой ссылки на документ в параметр приходит структура.

Рассмотрим на конкретном примере из конфигурации "Управление торговлей 11.4", почему так происходит и как исправить эту ситуацию. Проблема заключается не в ошибке платформы, а в особенностях логики самой типовой конфигурации, которые необходимо понимать при ее доработке.

Анализ проблемы: откуда берется структура вместо ссылки

Исходная задача — добавить свою логику после выполнения типовой процедуры ЗаполнитьПоЗаказуКлиента в модуле объекта документа "Операция по платежной карте". Разработчик создает расширение с процедурой-обработчиком, используя аннотацию &После.

Типовая процедура выглядит так:


Процедура ЗаполнитьПоЗаказуКлиента(Знач ДокументОснование, ДанныеЗаполнения)
    // ... логика заполнения документа ...
    
    // В этой точке ДокументОснование является ссылкой на документ
КонецПроцедуры

Код в расширении:


&После("ЗаполнитьПоЗаказуКлиента")
Процедура МояПроцедура_ЗаполнитьПоЗаказуКлиента(Знач ДокументОснование, ДанныеЗаполнения)
    // Ожидаем, что ДокументОснование - это ссылка,
    // но при отладке выясняется, что это Структура
    Сообщить(ТипЗнч(ДокументОснование)); // Выводит "Структура"
КонецПроцедуры

На первый взгляд, это кажется нелогичным. Параметр ДокументОснование передается по значению, что должно гарантировать его неизменность. Однако дьявол кроется в деталях, а именно — в том, как вызывается эта процедура в типовом коде.

Причина кроется в вызывающей процедуре ОбработкаЗаполнения. Проанализировав ее, мы находим ключевую строку кода:


Если ТипЗнч(ДанныеЗаполнения) = Тип("ДокументСсылка.ЗаказКлиента") Тогда
    ЗаполнитьПоЗаказуКлиента(ДанныеЗаполнения, ДанныеЗаполнения);
КонецЕсли;

Именно здесь и происходит "магия". Разберем последовательность действий по шагам:

  1. В процедуру ОбработкаЗаполнения приходит переменная ДанныеЗаполнения, которая на этом этапе содержит ссылку на документ "Заказ клиента".
  2. Вызывается процедура ЗаполнитьПоЗаказуКлиента. В оба ее параметра (ДокументОснование и ДанныеЗаполнения) передается одна и та же переменная.
  3. Первый параметр, Знач ДокументОснование, принимает значение этой переменной (ссылку на документ) и работает с ним как с локальной копией.
  4. Второй параметр, ДанныеЗаполнения, передается по ссылке (поскольку у него нет ключевого слова Знач). Внутри типовой процедуры ЗаполнитьПоЗаказуКлиента этому параметру присваивается новое значение — структура с данными для заполнения.
  5. Поскольку второй параметр был передан по ссылке, изменение его значения внутри процедуры приводит к изменению исходной переменной ДанныеЗаполнения в процедуре ОбработкаЗаполнения. Теперь она тоже хранит структуру.
  6. После того как основная процедура ЗаполнитьПоЗаказуКлиента завершила свою работу, платформа готовится вызвать обработчик расширения с аннотацией &После.
  7. Для вызова обработчика платформа берет те переменные, которые изначально передавались в основной метод. А переменная, которая передавалась в первый параметр (ДокументОснование), на данный момент уже изменилась (через второй параметр) и стала структурой.

В итоге, в расширение в параметр ДокументОснование попадает уже измененное значение, которое имеет тип Структура. Вот почему, несмотря на ключевое слово Знач, мы видим неожиданное изменение типа.

Способы решения проблемы

Поскольку аннотация &После выполняется уже после всех изменений, она не подходит для решения задачи, если нам нужен исходный документ-основание. Необходимо перехватывать управление раньше. Для этого существуют более подходящие аннотации.

Решение 1: Использование аннотации &Вместо

Этот способ дает полный контроль над вызовом типового метода. Мы можем перехватить вызов, сохранить нужные нам параметры, выполнить типовую логику и затем добавить свою.

Пример кода:


&Вместо("ЗаполнитьПоЗаказуКлиента")
Процедура МояПроцедура_ЗаполнитьПоЗаказуКлиента(Знач ДокументОснование, ДанныеЗаполнения)
    // На этом этапе ДокументОснование все еще является ссылкой.
    // Сохраним его в локальную переменную, чтобы не потерять.
    ИсходныйДокументОснование = ДокументОснование;

    // Вызываем стандартную процедуру, чтобы вся типовая логика отработала.
    // Параметры передаются точно так же, как они пришли к нам.
    ПродолжитьВызов(ДокументОснование, ДанныеЗаполнения);

    // После выполнения типового кода мы можем использовать сохраненное значение.
    // Здесь ИсходныйДокументОснование будет ссылкой на документ,
    // а параметр ДанныеЗаполнения уже станет структурой.
    Сообщить("Документ-основание: " + ИсходныйДокументОснование);
    // ... здесь ваша дополнительная логика ...
КонецПроцедуры

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

Решение 2: Использование аннотации &ИзменениеИКонтроль

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

Логика будет похожа на использование &Вместо: получить доступ к параметрам до их изменения, выполнить основную логику и затем добавить свою, используя сохраненные данные.

Ключевые выводы

  1. При работе с расширениями всегда анализируйте не только саму расширяемую процедуру, но и контекст ее вызова в типовой конфигурации. Часто причина неожиданного поведения кроется именно там.
  2. Аннотация &После получает на вход переменные в том состоянии, в котором они оказались после выполнения основного метода. Если они были изменены (даже косвенно, как в нашем примере), вы получите измененные значения.
  3. Для задач, где нужно получить доступ к исходным значениям параметров до их изменения в основной процедуре, используйте аннотации &Вместо или &ИзменениеИКонтроль.
  4. Помните о различиях передачи параметров по значению (Знач) и по ссылке (по умолчанию). Передача одной и той же переменной в оба типа параметров может привести к неочевидным последствиям.

Понимание этих механизмов поможет вам избежать ошибок и писать более надежный и предсказуемый код при доработке типовых конфигураций 1С с помощью расширений.

← На главную