привет всем :) это мой первый вопрос на 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/TChainTEventChain.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
, так как в нем есть информация о типе/количестве параметров и конвенции вызова фактического используемого события (в общем случае).