SeiOkami / OneS

Предложения по улучшению платформы
49 stars 3 forks source link

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

Open tormozit opened 1 year ago

tormozit commented 1 year ago

Уже есть заявка "Функции как объекты" 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

Hobbit-Jedi commented 1 year ago

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

tormozit commented 1 year ago

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

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

Hobbit-Jedi commented 1 year ago

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

Hobbit-Jedi commented 1 year ago

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

tormozit commented 1 year ago

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

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

Hobbit-Jedi commented 1 year ago

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

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

Например,

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

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

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

Nivanchenko commented 1 year ago

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

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

Или в идеале

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

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

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

SeiOkami commented 1 year ago

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

tormozit commented 1 year ago

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

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

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

tormozit commented 2 weeks ago

Все же в некоторых случаях, например при подключении обработчика ожидания, будет удобно сразу в ссылке на метод указать значения параметров для вызова. Поэтому добавим опциональный 3-й параметр в конструктор аналогично объекту ОписаниеОповещения: Новый СсылкаМетода(<ИмяМетода>, <КонтекстныйОбъект>[, <ЗначенияПараметров>])

tormozit commented 9 hours ago

Еще один костыльный способ вызывать по имени функцию с 3-мя параметрами (в 3-м передаваемая структура). Работает во всех контекстах при условии доступности экспорта методов.

Чт = Новый ЧтениеJSON;
Чт.УстановитьСтроку("{'r':1}");
я = ПрочитатьJSON(Чт,,,, "Функ", ЭтотОбъект, Струкутура);
Функция Функ(_1,_2, Струкутура) Экспорт
    Сообщить(1);
КонецФункции