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