OneS icon indicating copy to clipboard operation
OneS copied to clipboard

Простые исполняемые ссылки на методы

Open tormozit opened this issue 2 years ago • 15 comments

Уже есть заявка "Функции как объекты" 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 По сравнению с Выполнить("<ИмяМетода>(...)") вызов через ссылку будет безопаснее и главное - намного быстрее за счет отсутствия динамической компиляции, что важно при частых вызовах, например для расчета каких то свойств в элементах коллекций.

Еще часто предлагают ОписаниеОповещения в качестве ссылки на метод. Объясняю почему оно не подходит

  1. На сервере ОписаниеОповещения недоступно
  2. Набор параметров у метода-параметра фиксированные - обычно "Результат, ДопПараметры".

https://partners.v8.1c.ru/forum/topic/2128179

tormozit avatar Apr 27 '23 05:04 tormozit

А как предлагается использовать <КонтекстныйОбъект>?

Hobbit-Jedi avatar Apr 27 '23 06:04 Hobbit-Jedi

А как предлагается использовать <КонтекстныйОбъект>?

Если метод принадлежит модулю объекта (например СправочникОбъект или Форма), то вызывать его можно только от объекта, которому он принадлежит. Если конструктор вызывается внутри модуля, в котором есть метод-параметр, то можно не указывать объект - платформа сама его привяжет.

tormozit avatar Apr 27 '23 06:04 tormozit

А если мы создали ссылку на метод от объекта, а потом объект вышел из области видимости (удален уже из памяти), а мы вызываем метод по ссылке? Должно вывалиться красивое исключение?

Hobbit-Jedi avatar Apr 27 '23 07:04 Hobbit-Jedi

В-общем, примеров использования этого параметра не хватает.

Hobbit-Jedi avatar Apr 27 '23 07:04 Hobbit-Jedi

А если мы создали ссылку на метод от объекта, а потом объект вышел из области видимости (удален уже из памяти)

Удаление объекта из памяти в такой ситуации невозможно. Ссылка на объект в ссылке метода будет удерживать объект в памяти.

tormozit avatar Apr 27 '23 07:04 tormozit

Удаление объекта из памяти в такой ситуации невозможно. Ссылка на объект в ссылке метода будет удерживать объект в памяти.

Ладно. Не совсем из памяти.

Например,

&НаКлиенте
Процедура ОбработчикНажатияКнопки()
  Ф1 = ПолучитьСсылкуНаФункциюНаСервере();
  ВызовМетода Ф1(1, 2); // Здесь содержащего функцию объекта уже нет.
КонецПроцедуры

&НаСервере
Функция ПолучитьСсылкуНаФункциюНаСервере()
  ТекущийОбъект = РеквизитФормыВЗначение("Объект");
  Результат = Новый СсылкаМетода("РассчитатьСумму1",  ТекущийОбъект);
  Возврат Результат;
КонецФункции

Hobbit-Jedi avatar Apr 27 '23 07:04 Hobbit-Jedi

Я по сути предлагаю опрятную замену существующим приемам через "Выполнить()". Исключения будут возникать аналогичные. Если объект не существует в текущем клиент-серверном контексте, то и вызвать его метод нельзя - будет исключение выброшено. Но скорее всего тип СсылкаМетода просто сделают несериализуемым и потому его нельзя будет передавать между клиентом и сервером.

tormozit avatar Apr 27 '23 08:04 tormozit

Мне больше нравиться конструкция вызова, как то вот так:

СсылкаМетода.Выполнить(Параметры);

Или в идеале

СсылкаМетода = Новый СсылкаМетода(ИмяМетода, КонтекстныйОбъект);
СсылкаМетода(Параметры);

Nivanchenko avatar Apr 27 '23 09:04 Nivanchenko

СсылкаМетода.Выполнить(Параметры);

Согласен. Так не придется расширять синтаксис языка. Переделал на твой вариант.

tormozit avatar Apr 27 '23 10:04 tormozit

Думаю, что даже если на сервер добавят "ОписаниеОповещения", то этого бы хватило для большинства случаев. Объект можно передавать в качестве параметра. Да, описанное решение удобнее, но тут вопрос в сложности реализации на уровне платформы. Идеально всё же #14 , но если делать минимум, то можно просто ОписаниеОповещения на сервере позволять описывать.

SeiOkami avatar Apr 28 '23 05:04 SeiOkami

ОписаниеОповещения слишком неудобно, т.к.

  1. требует для всех методов делать обретки с двумя параметрами "Результат, ДопПараметры"
  2. требует сразу передать все параметры в месте создания "ссылки" на метод (по сути это не ссылка на метод, а описание конкретного вызова метода)

tormozit avatar Apr 28 '23 06:04 tormozit

Отправлено боту 28.04.2023

SeiOkami avatar Apr 28 '23 11:04 SeiOkami