Возможно ли создать экземпляр TRttiMethod для TRttiType с TypeKind=tkMethod?

привет всем :) это мой первый вопрос на stackoverflow :)

в Delphi XE2 RTTI есть класс TRttiMethod и в нем есть функция CreateImplementation(), которая позволяет динамически создавать процедуру или функцию с той же сигнатурой и анонимным методом в качестве ее тела.

используя TRttiContext.getMethods()/getMethod() мы можем получить коллекцию методов класса - коллекцию TRttiMethod.

в документации сказано (http://docwiki.embarcadero.com/VCL/en/RTTI.TRttiMethod) не создавать TRTTiMethod напрямую, а использовать getMethods() для получения его экземпляров.

таким образом, если у нас есть экземпляр TRTTIMethod, мы можем динамически создать метод с той же сигнатурой и вызвать его позже.

вопрос в том, что... например, у нас есть TNotifyEvent... TRttiContext.getType(typeinfo(TNotifyEvent)) возвращает TRttiType объект экземпляра, который имеет typekind = tkMethod. Мы знаем, что сигнатура TNotifyEvent - это процедура (sender : TObject) объекта;

можно ли получить TRttiMethod для этого? Я хочу динамически создать eventHandler для события, используя TRttiMethod.CreateImplementation() или может быть есть другой способ динамически создать реализацию метода?

PS: идея в том, чтобы создать что-то вроде цепочки событий. Во время компиляции мы не знаем тип/признак события, поэтому для этого можно использовать дженерики, например TEvenChain. Цепочка имеет коллекцию на зарегистрированных обработчиках событий, поэтому, поскольку мы не знаем тип обработчика во время компиляции, мы должны создать его динамически, и этот обработчик только получает свои параметры и вызывает зарегистрированный обработчик с ними.

update: вот некоторый код (идея заключается в создании цепочки обработчиков событий):

procedure TMainForm.FormCreate(Sender: TObject);
begin
    FEventChain := TNotifyChain.Create();
    FEventChain.AddHandler(event1);
    FEventChain.AddHandler(event2);   

    TestButton.OnClick := FEventChain.EventHandler;
end;

Event1 & event2 являются методами(sender : TObject) формы; это не общий пример (T в данном случае - NotifyEvent/TChain) TEventChain.EventHandler is TNotifyEvent too

TNotifyChain is

TNotifyChain = class(TObject)
  strict private
    FEvent : TNotifyEvent;
    FItems : TList;

    FCtx : TRttiContext;
  public
    procedure TestNotifyHandler(sender : TObject);   virtual; abstract;

    constructor Create();
    procedure AddHandler(eh : TNotifyEvent);
    property  EventHandler : TNotifyEvent read FEvent;
end;

EventHandler property is mapped to FEvent variable. А FEvent не присваивается, так как мы предполагаем, что тип события/хандлера неизвестен.

поэтому в конструкторе мы создаем виртуальный метод с сигнатурой TNotifyEvent и присваиваем его код FEvent:

constructor TNotifyChain.Create();
var st: TRttiType;
    //et : TRttiMethodType;
    Callback : TMethodImplementationCallback;
    rttiMethod : TRttiMethod;
    m : TMethod;
    mi : TMethodImplementation;
begin
    inherited;
    FItems := TList.Create();

    FCtx := TRttiContext.Create();

    //et := FCtx.GetType(typeinfo(TNotifyEvent)) as TRttiMethodType;
    st := FCtx.GetType(self.ClassType);


    rttiMethod := st.GetMethod('TestNotifyHandler');

    Callback := procedure(UserData: Pointer;
                        const Args: TArray;
                        out Result: TValue)
             var i : integer;
                 e : TMethod;
             begin

                for i := 0 to FItems.Count - 1 do begin
                    e := TMethod(Fitems[i]);
                    result := Invoke(e.Code, args, rttiMethod.CallingConvention, nil);
                end;
             end;


    mi := rttiMethod.CreateImplementation(self, Callback);

    m.data := self;
    m.Code := mi.CodeAddress;
    FEvent := TNotifyEvent(m);
end;

здесь я использую метод TestNotifyHandler для создания реального обработчика с той же сигнатурой с помощью TRttimethod.CreateImplementation()

так я полагаю, есть способ создать реализацию обработчика события во время выполнения, используя TRttiMethodType из TNotifyEvent, так как в нем есть информация о типе/количестве параметров и конвенции вызова фактического используемого события (в общем случае).

7
задан dummzeuch 18 January 2018 в 10:43
поделиться