TProc <TObject> для TNotifyEvent
В дополнение к этому сообщению принятый ответ остается очень загадочным:
@Button1.OnClick := pPointer(Cardinal(pPointer( procedure (sender: tObject) begin ((sender as TButton).Owner as TForm).Caption := 'Freedom to anonymous methods!' end )^ ) + $0C)^;
Интересно, можно ли придумать простейший и элегантный способ, похожий на:
Button.OnClick :=
AnonProc2NotifyEvent (
procedure (Sender: TObject)
begin
((Sender as TButton).Owner as TForm).Caption := 'Freedom to anonymous methods!'
end
);
чтобы достичь той же цели и где AnonProc2NotifyEvent - это метод владельца Button со следующей подписью:
TOwnerOfButton = class(TForm)
Button: TButton;
...
private
...
protected
function AnonProc2NotifyEvent(aProc: TProc<TObject>): TNotifyEvent;
public
...
end;
Возможно ли это, и если да, то как его реализовать?
Ответы
Ответ 1
Это сделает работу достаточно легко:
type
TNotifyEventWrapper = class(TComponent)
private
FProc: TProc<TObject>;
public
constructor Create(Owner: TComponent; Proc: TProc<TObject>);
published
procedure Event(Sender: TObject);
end;
constructor TNotifyEventWrapper.Create(Owner: TComponent; Proc: TProc<TObject>);
begin
inherited Create(Owner);
FProc := Proc;
end;
procedure TNotifyEventWrapper.Event(Sender: TObject);
begin
FProc(Sender);
end;
function AnonProc2NotifyEvent(Owner: TComponent; Proc: TProc<TObject>): TNotifyEvent;
begin
Result := TNotifyEventWrapper.Create(Owner, Proc).Event;
end;
Параметр Owner
в AnonProc2NotifyEvent
заключается в том, что можно управлять временем жизни объекта-обертки. Без чего-то подобного вы могли бы утечка экземпляров TNotifyEventWrapper
.
Передайте как Owner
, компонент, к которому вы подключаете событие. Например:
Button1.OnClick := AnonProc2NotifyEvent(
Button1,
procedure(Sender: TObject)
begin
(Sender as TButton).Caption := 'Clicked';
end
);
Итак, когда кнопка будет уничтожена, TNotifyEventWrapper
также будет уничтожен. Объект-оболочка должен проживать как минимум до тех пор, пока объект, к которому он связан, связан. И поэтому выбор Button1
как владельца является естественным и очевидным.
Ответ 2
Для справки, что я получаю, я изучил блог Барри Келли post, упомянутого выше в предыдущей публикации SO, и придумал это решение:
function TMainForm.Proc2NotifyEvent(const aProc: TNotifyReference): TNotifyEvent;
type
TVtable = array[0..3] of Pointer;
PVtable = ^TVtable;
PPVtable = ^PVtable;
begin
TMethod(Result).Code := PPVtable((@aProc)^)^^[3];
TMethod(Result).Data := Pointer((@aProc)^);
end;
Все еще загадочно, но инкапсулировано, что облегчает задачу кодера по сравнению с исходным методом.
Я попытался приукрасить MethRefToMethPtr и MakeNotify и поместить все в один метод.
Обратите внимание, что в сигнатуре метода было (небольшое) изменение, аргумент aProc стал const.