В практике разработки на платформе «1С:Предприятие 8» нередко возникают ситуации, когда имя вызываемого метода (функции или процедуры) становится известным только в момент выполнения кода. Это может быть связано с построением универсальных механизмов, обработок или при разработке гибких систем плагинов. Рассмотрим подробно, как реализовать такой вызов, какие инструменты предоставляет платформа и каких ошибок следует избегать.
Если нам необходимо вызвать функцию, которая должна вернуть какой-либо результат, наиболее подходящим инструментом является встроенная функция Вычислить(). Рассмотрим ситуацию: у нас есть переменная ИмяФункции, содержащая строку с названием метода, и нам нужно передать в неё параметр — ссылку на документ.
Проанализируем пример кода:
// Имя функции, которую нужно вызвать
ИмяФункции = "РассчитатьСуммуДокумента";
// Параметр, который мы передаем
ТекущаяСсылка = Документы.РеализацияТоваровУслуг.НайтиПоНомеру("001");
// Формируем строку вызова и получаем результат
Результат = Вычислить(ИмяФункции + "(ТекущаяСсылка)");
Обратим внимание на важный нюанс: в строке, которую мы передаем в Вычислить(), должны фигурировать имена переменных, которые доступны в текущем контексте. Платформа не сможет «прочитать» значение ссылки, если мы просто попытаемся преобразовать её в строку. Мы передаем именно имя переменной ТекущаяСсылка внутрь строки вызова.
В случаях, когда нам нужно вызвать процедуру (метод, который не возвращает значение), используется оператор Выполнить(). Он исполняет фрагмент кода, переданный ему в виде строки.
Разберем, как это работает на практике:
ИмяПроцедуры = "ОбщегоНазначения.ВывестиСообщениеПользователю";
ТекстСообщения = "Действие выполнено успешно!";
// Выполняем процедуру
Выполнить(ИмяПроцедуры + "(ТекстСообщения)");
Главное отличие Выполнить() от Вычислить() заключается в том, что первый метод предназначен для исполнения полноценных операторов языка, а второй — для вычисления выражений и получения их значений. Попытка присвоить результат работы Выполнить() переменной приведет к ошибке. Для пакетного запуска подобных скриптов подойдёт инструмент для выполнения произвольного кода в нескольких базах.
С развитием платформы (начиная с версии 8.3) и переходом на асинхронную модель работы в управляемых формах, появился объект ОписаниеОповещения. Хотя его основное предназначение — обработка завершения асинхронных вызовов, его можно использовать как удобную «обертку» для динамического вызова процедур.
Рассмотрим этот механизм подробнее:
ОписаниеОповещения, указывая имя процедуры и модуль.ВыполнитьОбработкуОповещения().
// Подготавливаем описание вызова
Оповещение = Новый ОписаниеОповещения("ИмяВашейПроцедуры", ЭтотОбъект);
// Параметры, которые будут переданы в процедуру
ПараметрыВызова = Новый Структура("Ссылка, Количество", ДокСсылка, 10);
// Осуществляем вызов
ВыполнитьОбработкуОповещения(Оповещение, ПараметрыВызова);
Этот способ считается более «чистым» с точки зрения архитектуры, так как он минимизирует использование строковых манипуляций с кодом и позволяет избежать некоторых проблем с безопасностью.
Иногда использование Вычислить() или Выполнить() запрещено корпоративными стандартами разработки из-за потенциальных уязвимостей (так называемых инъекций кода). В этом случае проанализируем альтернативный вариант — создание функции-диспетчера.
Выясним, как это выглядит в коде:
Функция ВызватьМетодДинамически(ИмяМетода, Параметр)
Если ИмяМетода = "РасчитатьСкидку" Тогда
Возврат РасчитатьСкидку(Параметр);
ИначеЕсли ИмяМетода = "ОпределитьНалог" Тогда
Возврат ОпределитьНалог(Параметр);
Иначе
ВызватьИсключение "Метод не найден!";
КонецЕсли;
КонецФункции
Такой подход делает код безопасным и легко отлаживаемым. Вы всегда видите список доступных для вызова методов и можете быть уверены, что система не выполнит произвольный вредоносный код, переданный в переменной.
При работе с динамическим исполнением кода важно учитывать несколько критических факторов:
Вычислить() строки, полученные напрямую от пользователя без предварительной проверки. Злоумышленник может передать строку вида "ЗавершитьРаботуСистемы()" или команду на удаление данных.Выполнить(). Если переменная ТекСсылка не определена в текущей процедуре, вызов Вычислить("МойМетод(ТекСсылка)") завершится ошибкой.ОбщегоНазначения. Там часто реализованы безопасные обертки для подобных вызовов, которые уже учитывают права доступа и профили безопасности.Подводя итог, можно сказать: если вам нужно быстрое решение «здесь и сейчас» внутри одного модуля, используйте Вычислить(). Если же вы проектируете сложную и безопасную систему, лучше присмотреться к ОписаниеОповещения или программному диспетчеру методов.