При разработке сложных и гибких систем на платформе «1С:Предприятие 8» мы часто сталкиваемся с необходимостью динамического взаимодействия между объектами. Типичный сценарий: в общий модуль передается управляемая форма или прикладной объект, и нам требуется вызвать определенный экспортный метод (процедуру или функцию), если он там реализован. Однако у некоторых форм или объектов этого метода может не быть.
Стандартный механизм платформы не предоставляет готовой функции для прямой рефлексии (отражения) — мы не можем просто вызвать метод вроде МетодСуществует(). В этой статье мы подробно разберем, как решить эту задачу изящно, безопасно и без нежелательного выполнения самого кода проверяемого метода.
Первое решение, которое приходит в голову — это использование стандартной конструкции Попытка...Исключение. Мы можем просто попытаться вызвать метод напрямую:
Попытка
Объект.ИмяЭкспортногоМетода();
Исключение
// Метод не существует или произошла ошибка
КонецПопытки;
Проанализируем, почему этот способ является плохим и небезопасным в реальной разработке:
Исключение. Программа посчитает, что метода просто нет, хотя он есть, но работает некорректно. Нам же нужно показать реальную ошибку выполнения пользователю или администратору.Выясним, как обойти эти ограничения и выполнить «чистую» проверку наличия метода без его запуска.
Для безопасной проверки в сообществе разработчиков 1С применяется оригинальный обходной путь. Он основан на поведении компилятора и парсера платформы 1С. Когда мы динамически вызываем метод через оператор Выполнить(), платформа сначала проверяет синтаксис и соответствие сигнатуры метода (количество параметров). Если передать в метод заведомо больше параметров, чем физически может поддерживать любая функция (например, 100 параметров), платформа выбросит исключение еще на этапе разбора вызова, даже не приступая к выполнению самого метода.
Давайте рассмотрим универсальную функцию, реализующую этот подход:
Функция МетодРеализован(Объект, Знач ИмяМетода) Экспорт
Если Объект = Неопределено Тогда
Возврат Ложь;
КонецЕсли;
// Формируем вызов метода с заведомо избыточным количеством параметров (более 80 запятых)
Запятые = ",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,";
Выражение = "Объект." + ИмяМетода + "(" + Запятые + ")";
ОписаниеОшибки = "";
Попытка
Выполнить(Выражение);
Исключение
ИнформацияОШибке = ИнформацияОбОшибке();
ОписаниеОшибки = ИнформацияОШибке.Описание;
КонецПопытки;
// Анализируем полученную системную ошибку
Если Найти(ОписаниеОшибки, "Метод объекта не обнаружен") > 0
Или Найти(ОписаниеОшибки, "Значение не является значением объектного типа") > 0 Тогда
// Метода гарантированно нет у данного объекта
Возврат Ложь;
КонецЕсли;
// Если ошибка связана с количеством параметров или типом вызова — метод существует!
Если Найти(ОписаниеОшибки, "Слишком много фактических параметров") > 0
Или Найти(ОписаниеОшибки, "Обращение к процедуре объекта как к функции") > 0 Тогда
Возврат Истина;
КонецЕсли;
// На случай англоязычной локализации платформы (клиентских лицензий или серверов)
Если Найти(НРег(ОписаниеОшибки), "too many actual parameters") > 0
Или Найти(НРег(ОписаниеОшибки), "method not found") > 0 Тогда
Возврат Найти(НРег(ОписаниеОшибки), "too many actual parameters") > 0;
КонецЕсли;
Возврат Ложь;
КонецФункции
Разберем по шагам логику работы функции МетодРеализован():
"Объект.ИмяМетода(,,,,,,,,,,,,,,,,,,,)".Выполнить().ИмяМетода у объекта нет, платформа генерирует исключение «Метод объекта не обнаружен» (или аналогичное англоязычное). Наша функция перехватывает его и возвращает Ложь.Истина).Несмотря на то, что «трюк с запятыми» работает надежно и проверен годами практики, в крупных тиражных решениях (например, в конфигурациях на базе 1С:БСП) разработчики стараются избегать динамического выполнения кода через Выполнить() из-за требований безопасности и производительности. Рассмотрим альтернативные варианты архитектуры.
Если вам нужно понять, поддерживает ли конкретная форма определенное поведение, мы можем проверять не наличие функции, а наличие реквизита или параметра формы. Для этого отлично подходит стандартный метод БСП:
Если ОбщегоНазначенияКлиентСервер.ЕстьРеквизитИлиСвойствоОбъекта(Форма, "ПараметрОсобойОбработки") Тогда
// Форма поддерживает нужный нам сценарий
КонецЕсли;
Этот способ работает быстрее, не использует компиляцию «на лету» и является абсолютно безопасным.
В Библиотеке стандартных подсистем часто применяется паттерн «Регистрация». Вместо того чтобы динамически искать экспортные функции у всех форм подряд, формы сами объявляют о своих возможностях при создании или инициализации. Например, через заполнение определенной структуры настроек или переопределение стандартных методов БСП (таких как ПриОпределенииНастроек).
Если перед вами стоит задача быстро и гарантированно проверить наличие экспортной процедуры или функции у произвольного объекта «в runtime», то «трюк с запятыми» через функцию Выполнить() является лучшим техническим решением. Он полностью защищает вашу систему от случайного выполнения бизнес-логики проверяемого метода и позволяет корректно обрабатывать ошибки времени выполнения.