Простые исполняемые ссылки на методы
Уже есть заявка "Функции как объекты" https://github.com/SeiOkami/OneS/issues/14 Но она очень сложная в восприятии и, уверен, в реализации. Я же предлагаю намного более простой и потому реалистичный вариант - только ссылки на методы.
Иногда в реальных задачах приходится передавать метод1 как параметр в метод2. Таким образом метод2 не знает заранее какой конкретно метод он будет звать, но знает какие ему параметры передать. Например в БСП есть метод ОбновлениеИнформационнойБазы.ОбъектОбработан(), где в цикле вызывается ряд совпадающих по набору параметров динамически определяемых методов-проверок
Вычислить(СвойстваОбработчика.ПроцедураПроверки + "(МетаданныеИОтбор)")
У такого подхода есть неудобства:
- При каждом выполнении этого кода делается компиляция текста программы, на что уходит относительно много времени и потому при частом выполнении будет заметное замедление по сравнению с однократно компилируемым вызовом.
- Не работает в вебклиенте
- К такому вызову могут быть нарекания по безопасности (использование Выполнить() и Вычислить() может быть запрещено в профиле безопасности)
- Вызов из динамического кода трудно читаем в стеке, описании ошибки и усложняет отладку
Чтобы обойти эти проблемы и сделать подобный код более читаемым предлагаю добавить во встроенный язык простые исполняемые ссылки на методы.
Создаем ссылку метода через
Новый СсылкаМетода(<ИмяМетода>, <КонтекстныйОбъект>)
, где ИмяМетода - имя метода модуля или платформы, КонтекстныйОбъект - объект, которому принадлежит контекстный метод.
Вызываем метод через ссылку двумя способами
<СсылкаМетода>.Выполнить(<ПараметрыЧерезЗапятую>)
<СсылкаМетода>.ВыполнитьСтруктурой(<СтруктураПараметров>)
Это похоже на упрощенные делегаты (без обозначения параметров) других языков программирования. В качестве бонуса мы получаем
- возможность удобно вызывать любой метод с передачей параметров в виде структуры без дополнительных манипуляций
- команда конфигуратора "Найти использование" для имени метода будет отображать такие строки с конструкторами ссылок на этот метод
- команда конфигуратора "Рефакторинг/Переименовать" для имени метода будет обновлять имя метода внутри конструкторов ссылок на него автоматически
- команда "Перейти к определению" на имени метода в конструкторе ссылки на него будет переходить к определению метода
Пример
Функция РассчитатьСумму1(Число1, Число2) Экспорт
Возврат (Число1 + Число2) * 0.1;
КонецФункции
Функция РассчитатьСумму2(Число1, Число2) Экспорт
Возврат (Число1 - Число2) * Число2;
КонецФункции
Процедура РассчитатьВТаблице(Таблица, ФункцияРасчетаПоСтроке)
Для Каждого Строка Из Таблица Цикл
Строка.Сумма = ФункцияРасчетаПоСтроке.Выполнить(Строка.Число1, Строка.Число2);
КонецЦикла;
КонецПроцедуры
Процедура Тест()
Ф1 = Новый СсылкаМетода("РассчитатьСумму1");
Сообщить(Ф1.Выполнить(1, 2));
РассчитатьВТаблице(ТЗ, Ф1);
РассчитатьВТаблице(ТЗ, Новый СсылкаМетода("РассчитатьСумму2"));
КонецПроцедуры
https://paste1c.ru/3z4hhwyxatoc По сравнению с Выполнить("<ИмяМетода>(...)") вызов через ссылку будет безопаснее и главное - намного быстрее за счет отсутствия динамической компиляции, что важно при частых вызовах, например для расчета каких то свойств в элементах коллекций.
Еще часто предлагают ОписаниеОповещения в качестве ссылки на метод. Объясняю почему оно не подходит
- На сервере ОписаниеОповещения недоступно
- Набор параметров у метода-параметра фиксированные - обычно "Результат, ДопПараметры".
https://partners.v8.1c.ru/forum/topic/2128179
А как предлагается использовать <КонтекстныйОбъект>?
А как предлагается использовать <КонтекстныйОбъект>?
Если метод принадлежит модулю объекта (например СправочникОбъект или Форма), то вызывать его можно только от объекта, которому он принадлежит. Если конструктор вызывается внутри модуля, в котором есть метод-параметр, то можно не указывать объект - платформа сама его привяжет.
А если мы создали ссылку на метод от объекта, а потом объект вышел из области видимости (удален уже из памяти), а мы вызываем метод по ссылке? Должно вывалиться красивое исключение?
В-общем, примеров использования этого параметра не хватает.
А если мы создали ссылку на метод от объекта, а потом объект вышел из области видимости (удален уже из памяти)
Удаление объекта из памяти в такой ситуации невозможно. Ссылка на объект в ссылке метода будет удерживать объект в памяти.
Удаление объекта из памяти в такой ситуации невозможно. Ссылка на объект в ссылке метода будет удерживать объект в памяти.
Ладно. Не совсем из памяти.
Например,
&НаКлиенте
Процедура ОбработчикНажатияКнопки()
Ф1 = ПолучитьСсылкуНаФункциюНаСервере();
ВызовМетода Ф1(1, 2); // Здесь содержащего функцию объекта уже нет.
КонецПроцедуры
&НаСервере
Функция ПолучитьСсылкуНаФункциюНаСервере()
ТекущийОбъект = РеквизитФормыВЗначение("Объект");
Результат = Новый СсылкаМетода("РассчитатьСумму1", ТекущийОбъект);
Возврат Результат;
КонецФункции
Я по сути предлагаю опрятную замену существующим приемам через "Выполнить()". Исключения будут возникать аналогичные. Если объект не существует в текущем клиент-серверном контексте, то и вызвать его метод нельзя - будет исключение выброшено. Но скорее всего тип СсылкаМетода просто сделают несериализуемым и потому его нельзя будет передавать между клиентом и сервером.
Мне больше нравиться конструкция вызова, как то вот так:
СсылкаМетода.Выполнить(Параметры);
Или в идеале
СсылкаМетода = Новый СсылкаМетода(ИмяМетода, КонтекстныйОбъект);
СсылкаМетода(Параметры);
СсылкаМетода.Выполнить(Параметры);
Согласен. Так не придется расширять синтаксис языка. Переделал на твой вариант.
Думаю, что даже если на сервер добавят "ОписаниеОповещения", то этого бы хватило для большинства случаев. Объект можно передавать в качестве параметра. Да, описанное решение удобнее, но тут вопрос в сложности реализации на уровне платформы. Идеально всё же #14 , но если делать минимум, то можно просто ОписаниеОповещения на сервере позволять описывать.
ОписаниеОповещения слишком неудобно, т.к.
- требует для всех методов делать обретки с двумя параметрами "Результат, ДопПараметры"
- требует сразу передать все параметры в месте создания "ссылки" на метод (по сути это не ссылка на метод, а описание конкретного вызова метода)
Отправлено боту 28.04.2023