Итак, задача — система, позволяющая создавать пользовательские функции для SQL-БД. Функции пишутся на дотнете. Функции бывают двух видов — скалярные и агрегаты. Допускается перегрузка функций. Агрегаты фактически представляют собой две функции — одна вызывается по ходу вычислений, а другая только один раз, в конца (таков дизайн БД, я тут не причем).
Пока останавливаюсь на таком:
1. Для создания собственной функции необходимо реализовать интерфейс IFunction. На настоящий момент интерфейс получает универсальным — и для скалярных, и для агрегатов:
public interface IFunction
{
void Execute(CallType call, IFunctionContext context);
}
CallType — энумерация, которая показывает, в каком качестве вызывается функция:
public enum CallType
{
Normal,
Step,
Final
}
С одной стороны вроде и неплохо. С другой — как-то не очень. Во-первых, единственный смысл этой функции отличить вызов "обычной" скалярной функции от одного из вызовов в цепочке агрегата. Далее — единственный интерфейс для функций, принцип работы которых сильно отличается тоже не очень.
Логически высматривается такая картинка:
И регистрация функций будет выглядеть красиво. Но, во-первых, IFunction получается пустым. Ну просто нечего туда засунуть. И как-то некрасиво это. Во-вторых интерфейсы тоже выглядят довольно странно:
Изящно, не правда ли? Предназначение этого интерфейса, думаю, вполне понятно по объявлению, но уже как-то он "смешивает напитки". Хотелось бы как-то это поменять...
Заранее оговорюсь, что регистрацию через лямбду я не рассматриваю, т.к. на мой взгляд это весьма частный случай и далеко не весь код будет выглядеть понятно и красиво при передаче лямбды. Поэтому мне кажется, что регистрация через лямбду должна быть реализована как "фишка" поверх описываемой здесь инфраструктуры.
Здравствуйте, gandjustas, Вы писали:
ВВ>>Пока все. Буду рад любым советам. G>А чем не подхоит способ как в SQL CLR?
Это такой:
class SomeClass
{
[SqlFunction]
int Abs(int val)
{
return Math.Abs(val);
}
}
?
Такой вариант имеется в виду, но есть ряд нюансов.
Основная проблема — чтобы обеспечить вызов такой функции и не получить при этом мега-тормоза, придется джитить код. И потом не очень ясно, как быть с агрегатами.
Наконец, а насколько это удобно? Т.е. implementation cost — немалький. А кардинальный отличий от своего варианта я не вижу. Кардинально удобнее для вышеозначенного примера было бы сделать:
RegisterFunction("Abs", i => Math.Abs(i));
А если код ф-ции большой и сложный, то не по фигу — интерфейс ли реализовать или атрибутом ее пометить? Лучше уж я код поджитю, чтобы осущетсвить поддержку лямб как в примере выше — уже поверх всех своих интерфейсов.
Или нет?
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Здравствуйте, gandjustas, Вы писали:
ВВ>>>Пока все. Буду рад любым советам. G>>А чем не подхоит способ как в SQL CLR?
ВВ>Это такой:
ВВ>
ВВ>Такой вариант имеется в виду, но есть ряд нюансов. ВВ>Основная проблема — чтобы обеспечить вызов такой функции и не получить при этом мега-тормоза, придется джитить код.
Я думаю динамической кодогенерацией рано или поздно придется заниматься в целях быстройдействия. На первых порах можно огранииться делегатами.
ВВ>И потом не очень ясно, как быть с агрегатами.
Также как в SQL CLR.
ВВ>Наконец, а насколько это удобно? Т.е. implementation cost — немалький. А кардинальный отличий от своего варианта я не вижу.
А чем implementation cost немаленький. Вместо ссылки на интерфейс держать ссылку на делегат.
ВВ>Кардинально удобнее для вышеозначенного примера было бы сделать: ВВ>
ВВ>RegisterFunction("Abs", i => Math.Abs(i));
ВВ>
Такой вариант имхо тоже стоит поддерживать, более того вариант с атрибутом можно сделать через такую функцию.
Я для таких целей предпочитаю делать сначала императивный API, потом на его базе достраивать декларативный на атрибутах или с внешним конфигом.
ВВ>А если код ф-ции большой и сложный, то не по фигу — интерфейс ли реализовать или атрибутом ее пометить? Лучше уж я код поджитю, чтобы осущетсвить поддержку лямб как в примере выше — уже поверх всех своих интерфейсов. ВВ>Или нет?
1)Пустой (маркерный) интерфейс семантически эквивалентен атрибуту
2)Атрибут позволяет передать больше информации
3)Использование интерфейсов приводит к использованию экземпляров объектов, что создает вопросы о времени жизни и конкурентном доступе к данным, статические функции выгоднее в этом плане
4)С помощью статических функций можно обеспечить работу Linq-провайдера с этими же функциями.
Здравствуйте, gandjustas, Вы писали:
ВВ>>Такой вариант имеется в виду, но есть ряд нюансов. ВВ>>Основная проблема — чтобы обеспечить вызов такой функции и не получить при этом мега-тормоза, придется джитить код. G>Я думаю динамической кодогенерацией рано или поздно придется заниматься в целях быстройдействия. На первых порах можно огранииться делегатами.
Делегаты я не понял как помогут. См. ниже.
ВВ>>И потом не очень ясно, как быть с агрегатами. G>Также как в SQL CLR.
Ну здесь бек-энд немного другой. Я говорил — изначально для агрегата предполагаются две ф-ции — Step и Final. Вполне можно сделать и так, что агрегат будет одной функцией, имеющей состояние — и передавать это состояние каким-либо способом. Можно просто сообщать с помощью флажка о том, что происходит "последний" вызов (правда, при этом аргументов не будет). Можно сделать две функции как в самой базе.
ВВ>>Наконец, а насколько это удобно? Т.е. implementation cost — немалький. А кардинальный отличий от своего варианта я не вижу. G>А чем implementation cost немаленький. Вместо ссылки на интерфейс держать ссылку на делегат.
А откуда у меня делегат-то возьмется? У меня ж произвольная сигнатура метода. Его тупо через рефлекшин придется дергать. Да и в любом случае — даже если изловчиться как-то прикрутить сюда делегат — его придется вызывать через Invoke — и по сути хрен редьки не слаще. Потом очевидно же подобное решение с т.з. производительности неприемлимо совершенно. С интерфейсами — еще более или менее. По крайней мере можно первую версию выкатить, которая "терпит" interface call footprint. (Например, на древнем Целероне 2400 я разницу между прямым вызовом и интерфейсом вижу где-то на миллионе итераций, а рефлекшин сразу по глазам бьет).
ВВ>>Кардинально удобнее для вышеозначенного примера было бы сделать: ВВ>>
ВВ>>RegisterFunction("Abs", i => Math.Abs(i));
ВВ>>
G>Такой вариант имхо тоже стоит поддерживать, более того вариант с атрибутом можно сделать через такую функцию. G>Я для таких целей предпочитаю делать сначала императивный API, потом на его базе достраивать декларативный на атрибутах или с внешним конфигом.
Вот, собственно, об этом и речь. Императивное АПИ — это ведь не всякие там штучки с атрибутами. Собственно, что атрибут, что лямбда — это примочки для регистрации функции. Моя мысль — сделать изначально некоторую модель, поверх которой уже накручивать всю красоту. И тогда мы возвращаемся к изначальному вопросу.
ВВ>>А если код ф-ции большой и сложный, то не по фигу — интерфейс ли реализовать или атрибутом ее пометить? Лучше уж я код поджитю, чтобы осущетсвить поддержку лямб как в примере выше — уже поверх всех своих интерфейсов. ВВ>>Или нет? G>1)Пустой (маркерный) интерфейс семантически эквивалентен атрибуту
Я и не хочу пустой интерфейс.
G>2)Атрибут позволяет передать больше информации
Как видно из моих примеров — вся информация сводится к: имя, кол-во параметров, тип ф-ции. Все.
G>3)Использование интерфейсов приводит к использованию экземпляров объектов, что создает вопросы о времени жизни и конкурентном доступе к данным, статические функции выгоднее в этом плане G>4)С помощью статических функций можно обеспечить работу Linq-провайдера с этими же функциями.
А причем тут интерфейсы, статические функции? Это, мне кажется, клиентский код должен решать что и как ему удобнее. Интерфейсы тоже не обязывают к созданию экземпляров — можно сделать синглтон.
Потом я не понял — что все-таки ты предлагаешь? Сам же говоришь: "Я для таких целей предпочитаю делать сначала императивный API". Как будет выглядеть этот императивный API?
Здравствуйте, Воронков Василий, Вы писали:
ВВ>>>И потом не очень ясно, как быть с агрегатами. G>>Также как в SQL CLR.
ВВ>Ну здесь бек-энд немного другой. Я говорил — изначально для агрегата предполагаются две ф-ции — Step и Final. Вполне можно сделать и так, что агрегат будет одной функцией, имеющей состояние — и передавать это состояние каким-либо способом. Можно просто сообщать с помощью флажка о том, что происходит "последний" вызов (правда, при этом аргументов не будет). Можно сделать две функции как в самой базе.
Да посмотри уже как это в SQL CLR делается.
ВВ>>>Наконец, а насколько это удобно? Т.е. implementation cost — немалький. А кардинальный отличий от своего варианта я не вижу. G>>А чем implementation cost немаленький. Вместо ссылки на интерфейс держать ссылку на делегат. ВВ>А откуда у меня делегат-то возьмется?
Delegate.CreateDelegate
ВВ>У меня ж произвольная сигнатура метода.
Не совсем, должны быть какие-то соглашения о параметрах метода, вполне возможно что будет именно фиксированная сигнатура.
ВВ>Его тупо через рефлекшин придется дергать.
Delegate.DynamicInvoke или писать (взять из интернета) обертку для типизированных вызовов с помощью DynamicMethod.
ВВ>Да и в любом случае — даже если изловчиться как-то прикрутить сюда делегат — его придется вызывать через Invoke — и по сути хрен редьки не слаще.
IFunction и IFunctionContext фактически моделируют тоже поведение делегата.
только писать функции немного сложнее будет так как вся "грязная" работа по приведению типов ложится на сам метод.
ВВ>Потом очевидно же подобное решение с т.з. производительности неприемлимо совершенно. С интерфейсами — еще более или менее.
А замеры проводились?
Все равно вы придете к динамической кодогенерации, а там уже IFunctionContext станет излишним.
ВВ>А причем тут интерфейсы, статические функции? Это, мне кажется, клиентский код должен решать что и как ему удобнее. Интерфейсы тоже не обязывают к созданию экземпляров — можно сделать синглтон. ВВ>Потом я не понял — что все-таки ты предлагаешь? Сам же говоришь: "Я для таких целей предпочитаю делать сначала императивный API". Как будет выглядеть этот императивный API?
так и будет:
RegisterFunction(MethodInfo, other params)
RegisterAggregate(Type,other params) или RegisterAggregate(MethodInfo acc, MethodInfo merge, other params)
Здравствуйте, gandjustas, Вы писали:
G>Да посмотри уже как это в SQL CLR делается.
Я повторюсь — у меня не SQL Server. И способ исполнения агрегата отличается.
ВВ>>У меня ж произвольная сигнатура метода. G>Не совсем, должны быть какие-то соглашения о параметрах метода, вполне возможно что будет именно фиксированная сигнатура.
Что значит фиксированная сигнатура? На хрена тогда все это нужно? Фиксированная сигнатура это:
MyFunc(params object[] args);
и даже никаких делегатов не надо.
Смысл того "как это в SQL CLR делается" именно в том, что сигнатура произвольная + строгая типизация.
ВВ>>Потом очевидно же подобное решение с т.з. производительности неприемлимо совершенно. С интерфейсами — еще более или менее. G>А замеры проводились?
Я мерил. Накладные расходы на интерфейс не очень сильные и начинают становиться заметны, когда вызовов *очень* много. А если в запросе будет происходить скан миллиона записей, думаю миллион вызовов через интерфейс покажется назметными с т.з. стоимости.
В любом случае заниматься подобной оптимизацией с самого начала мне кажется избыточным.
G>Все равно вы придете к динамической кодогенерации, а там уже IFunctionContext станет излишним.
Ну вот в SQL CLR он совсем нелишний, кстати.
G>так и будет: G>RegisterFunction(MethodInfo, other params) G>RegisterAggregate(Type,other params) или RegisterAggregate(MethodInfo acc, MethodInfo merge, other params)
Т.е. вы предлагаете вместо того, чтобы клиент писал:
И при это все равно сигнатура функции будет фиксированной?
По-моему мой IFunctionContext по сравнению с этим ну просто Мона Лиза
Re: Дизайн интерфейса
От:
Аноним
Дата:
13.06.09 14:31
Оценка:
По-моему, то что вы пытаетесь сделать, этот «интерфейс» — всего лишь пародия на рефлексию.
Если вы начнете это дальше развивать, то получите жалкое подобие того, что уже (отлично) реализовано в классе System.Reflection.MethodInfo (и других классах этого пространства имен).
Почему бы просто не использовать класс MethodInfo?
Здравствуйте, Аноним, Вы писали:
А>По-моему, то что вы пытаетесь сделать, этот «интерфейс» — всего лишь пародия на рефлексию.
А>Если вы начнете это дальше развивать, то получите жалкое подобие того, что уже (отлично) реализовано в классе System.Reflection.MethodInfo (и других классах этого пространства имен).
А>Почему бы просто не использовать класс MethodInfo?
Класс MethodInfo — это "контейнер", содержащий метаданные. Интерфейс IFunction — реализация функции. И как раз метаданных о функции он не содержит вообще. Что между ними общего?
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Здравствуйте, gandjustas, Вы писали:
G>>Да посмотри уже как это в SQL CLR делается.
ВВ>Я повторюсь — у меня не SQL Server. И способ исполнения агрегата отличается.
Ну если SQL база данных, то все равно стадии инциализации, накопления и финализации есть.
ВВ>>>Потом очевидно же подобное решение с т.з. производительности неприемлимо совершенно. С интерфейсами — еще более или менее. G>>А замеры проводились?
ВВ>Я мерил. Накладные расходы на интерфейс не очень сильные и начинают становиться заметны, когда вызовов *очень* много. А если в запросе будет происходить скан миллиона записей, думаю миллион вызовов через интерфейс покажется назметными с т.з. стоимости.
надо померить не стоимость вызова (и так понятно, что интерфейсный вызов дешевле), а время работы агрегата.
IFunctionContext с его методами будет вносить оверхед, который даже при кодогенерации не исчезнет.
ВВ>В любом случае заниматься подобной оптимизацией с самого начала мне кажется избыточным.
Ну так в том и дело, что использование IFunctionContext — оптимизация, ограничивающая пространство маневра.
G>>Все равно вы придете к динамической кодогенерации, а там уже IFunctionContext станет излишним. ВВ>Ну вот в SQL CLR он совсем нелишний, кстати.
В каком месте?
G>>так и будет: G>>RegisterFunction(MethodInfo, other params) G>>RegisterAggregate(Type,other params) или RegisterAggregate(MethodInfo acc, MethodInfo merge, other params)
ВВ>Т.е. вы предлагаете вместо того, чтобы клиент писал:
ВВ>
я этого не предлагаю. Я дал пример самого низкого уровня API.
ВВ>По-моему мой IFunctionContext по сравнению с этим ну просто Мона Лиза
Ага, загадочно улыбается.
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Здравствуйте, Аноним, Вы писали:
А>>По-моему, то что вы пытаетесь сделать, этот «интерфейс» — всего лишь пародия на рефлексию.
А>>Если вы начнете это дальше развивать, то получите жалкое подобие того, что уже (отлично) реализовано в классе System.Reflection.MethodInfo (и других классах этого пространства имен).
А>>Почему бы просто не использовать класс MethodInfo?
ВВ>Класс MethodInfo — это "контейнер", содержащий метаданные. Интерфейс IFunction — реализация функции. И как раз метаданных о функции он не содержит вообще. Что между ними общего?
Насчет MethodInfo — мимо кассы, но суть вполне правильно указана. Все эти интерфейсы делаются чтобы получить механизм, аналогичный рефлексии.
Здравствуйте, gandjustas, Вы писали:
ВВ>>Я повторюсь — у меня не SQL Server. И способ исполнения агрегата отличается. G>Ну если SQL база данных, то все равно стадии инциализации, накопления и финализации есть.
Блин, я же писал — есть две стадии Step и Final. Можно их "смержить" до одного метода, используя специальный флажок для определения стадии. Собственно, по этому поводу я также и хотел посоветоваться.
ВВ>>В любом случае заниматься подобной оптимизацией с самого начала мне кажется избыточным. G>Ну так в том и дело, что использование IFunctionContext — оптимизация, ограничивающая пространство маневра.
Это не оптимизация. Если посмотреть на интерфейс, то можно увидеть, что это реализация необходимо функциональности "в лоб". И не более того. Мне интересно обсудить сам контракт, а не тот факт интерфейс это или нет. Скорее всего это и не будет интерфейсом.
G>>>Все равно вы придете к динамической кодогенерации, а там уже IFunctionContext станет излишним. ВВ>>Ну вот в SQL CLR он совсем нелишний, кстати. G>В каком месте?
SqlContext.
Вообще помимо собственно параметров уже сейчас есть такие задачи как сообщить базе об ошибке и пр. В дальнейшем могут появиться и другие — получить текст запроса и пр. Все равно какой-то "контекст" в том или ином виде нужен.
G>я этого не предлагаю. Я дал пример самого низкого уровня API.
Что мне с этим АПИ делать? Я так и не понял, извините, какой смысл
1) фиксировать сигнатуру методов
2) регистрировать их — неважно на низком или высоком уровне — в виде МетодИнфо.
Здравствуйте, gandjustas, Вы писали:
G>Насчет MethodInfo — мимо кассы, но суть вполне правильно указана. Все эти интерфейсы делаются чтобы получить механизм, аналогичный рефлексии.
Мне всегда казалось, что рефлексия — это прежде всего механизм получения метаданных типов. Возможность создания экземпляров, вызовов — это уже так, фишка. Мне также казалось, у меня совсем другая задача — определить контракт для вызова.
Я в упор не понимаю, почему вы предлагаете мне тут рефлекшин. Как говорится, недостатки очевидны, а бенефиты вы, извините, мне так и не смогли объяснить.
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Здравствуйте, gandjustas, Вы писали:
ВВ>>>Я повторюсь — у меня не SQL Server. И способ исполнения агрегата отличается. G>>Ну если SQL база данных, то все равно стадии инциализации, накопления и финализации есть.
ВВ>Блин, я же писал — есть две стадии Step и Final. Можно их "смержить" до одного метода, используя специальный флажок для определения стадии. Собственно, по этому поводу я также и хотел посоветоваться.
Ну правльно. И варианта 2: регистрировать методы парами, объединить их в один класс, реализующий како-либо контракт (явный через интерфейс или неявный).
ВВ>>>В любом случае заниматься подобной оптимизацией с самого начала мне кажется избыточным. G>>Ну так в том и дело, что использование IFunctionContext — оптимизация, ограничивающая пространство маневра.
ВВ>Это не оптимизация. Если посмотреть на интерфейс, то можно увидеть, что это реализация необходимо функциональности "в лоб".
Ну как-то неправдоподобно. Вот есть в sql функция func(x:int):int, тогда реалзиация в языке "влоб" будет int func(int x).
А за всю гряхную работу по протаскиванию значений от sql в реализацию должен делать код, о котором функция func и не знает.
ВВ>И не более того. Мне интересно обсудить сам контракт, а не тот факт интерфейс это или нет. Скорее всего это и не будет интерфейсом.
Сам контракт — контракт функции.
G>>>>Все равно вы придете к динамической кодогенерации, а там уже IFunctionContext станет излишним. ВВ>>>Ну вот в SQL CLR он совсем нелишний, кстати. G>>В каком месте?
ВВ>SqlContext. ВВ>Вообще помимо собственно параметров уже сейчас есть такие задачи как сообщить базе об ошибке и пр. В дальнейшем могут появиться и другие — получить текст запроса и пр. Все равно какой-то "контекст" в том или ином виде нужен.
Ну это сильно сервисный класс, котороый необходим только в триггерах, видимо из-за их непродуманности.
Функции и агрегаты вполне могут и не обращаться к этому классу.
G>>я этого не предлагаю. Я дал пример самого низкого уровня API.
ВВ>Что мне с этим АПИ делать?
Ничего, я просто первое что я придумал.
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Здравствуйте, gandjustas, Вы писали:
G>>Насчет MethodInfo — мимо кассы, но суть вполне правильно указана. Все эти интерфейсы делаются чтобы получить механизм, аналогичный рефлексии.
ВВ>Мне всегда казалось, что рефлексия — это прежде всего механизм получения метаданных типов. Возможность создания экземпляров, вызовов — это уже так, фишка. Мне также казалось, у меня совсем другая задача — определить контракт для вызова.
Его не нуно определять, контракт уже есть у метода. Есдинственный способ работать с таким контрактом — рефлекшн.
ВВ>Я в упор не понимаю, почему вы предлагаете мне тут рефлекшин. Как говорится, недостатки очевидны, а бенефиты вы, извините, мне так и не смогли объяснить.
Недостатки у рефлекшена только в скорости, но это лечится динамической кодогенерацией.
Re[3]: Дизайн интерфейса
От:
Аноним
Дата:
14.06.09 11:14
Оценка:
ВВ>Класс MethodInfo — это "контейнер", содержащий метаданные. Интерфейс IFunction — реализация функции. И как раз метаданных о функции он не содержит вообще. Что между ними общего?
Это как так «не содержит»?! А IFunctionContext — это что, не метаданные? Это самые что ни на есть натуральные метаданные, представляющие собой жалкую пародию на класс MethodInfo.
А MethodInfo можно использовать примерно так (саму задачу вы таки не описали, так что конкретнее привести пример не могу):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace UserMethods
{
class UserFunction
{
public UserFunction(object targetObject, string methodName)
{
this.targetObject = targetObject;
methodInfo = targetObject.GetType().GetMethod(methodName);
}
public void Invoke()
{
methodInfo.Invoke(targetObject, null);
}
object targetObject;
MethodInfo methodInfo;
}
class DB
{
public void RegisterUserFunction(object targetObject, string name)
{
userFunctions.Add(new UserFunction(targetObject, name));
}
public void InvokeUserFunctions()
{
userFunctions.ForEach(userFunction => userFunction.Invoke());
}
List<UserFunction> userFunctions = new List<UserFunction>();
}
class SomeUserClass
{
public void SomeUserFunction()
{
Console.WriteLine("User function is being called.");
}
}
class Program
{
static void Main(string[] args)
{
DB db = new DB();
SomeUserClass userObject = new SomeUserClass();
db.RegisterUserFunction(userObject, "SomeUserFunction");
db.InvokeUserFunctions();
}
}
}
Re[5]: Дизайн интерфейса
От:
Аноним
Дата:
14.06.09 12:17
Оценка:
ВВ>Мне всегда казалось, что рефлексия — это прежде всего механизм получения метаданных типов. Возможность создания экземпляров, вызовов — это уже так, фишка.
У вас превратное представление о метаданных. Вы не задумаывались, для чего вообще в дотнете используются метаданные? Вот, например, в C++ вообще нет метаданных (что очень плохо), и он без них работает.
По моей информации, полученной из надежных источников, основной областью применения метаданных в дотнете является динамическая кодогенерация, также известная как JIT, а также поддержка компонентной модели.
А в самом деле, зачем мне нужно получать метаданные, если я с ними ничего потом не смогу сделать?
Здравствуйте, Аноним, Вы писали:
А>У вас превратное представление о метаданных.
Да нет, похоже, это у вас.
А>Вы не задумаывались, для чего вообще в дотнете используются метаданные?
Для получения информации о типах, очевидно.
А>Вот, например, в C++ вообще нет метаданных (что очень плохо), и он без них работает.
Откройте для себя RTTI.
А>По моей информации, полученной из надежных источников, основной областью применения метаданных в дотнете является динамическая кодогенерация, также известная как JIT, а также поддержка компонентной модели.
А вот за эту фразу вам большое спасибо. Улыбнуло. Сильно.
А>А в самом деле, зачем мне нужно получать метаданные, если я с ними ничего потом не смогу сделать?
Не можете ничего сделать как раз потому что у вас превратное представление о метаданных.
Здравствуйте, Аноним, Вы писали:
А>Это как так «не содержит»?! А IFunctionContext — это что, не метаданные? Это самые что ни на есть натуральные метаданные, представляющие собой жалкую пародию на класс MethodInfo.
Нет, это не метаданные. Это колл-бек интерфейс. Разницу понимаете?
А>А MethodInfo можно использовать примерно так (саму задачу вы таки не описали, так что конкретнее привести пример не могу):
Задачу я описал — механизм регистрации скалярных и агрегатных ф-ций для СКЛ БД.
[skipped]
За пример кода спасибо
Да, а потом мы еще удивляемся — а че все так работает-то медленно? Видимо, дотнет во всем виноват
Теперь методы интерфейса стали похожи на обычный вызов функции, что в общем-то логично
Описаны параметры (IFunctionParams) и возвращаемый результат (IFunctionResult).
А вот, что в этих интерфейсах должно быть — это вопрос, который непосредственно завязан на методы IFunctionContext.
Непонятно, например, зачем отдельно выделять SetResultValue<T>, void SetResultBlob, SetResultNull, SetResultZeroBlob.
И непонятны отличия ReportError(FunctionError error) и ReportError(int code, string message).