继续这个post,其接受的答案仍然非常神秘:
@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;
这是否可行,如果可行,如何实施呢?
答案 0 :(得分:28)
这将很容易做到这一点:
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
作为所有者的选择是自然而明显的。
答案 1 :(得分:5)
为了参考这个我得到的内容,我研究了Barry Kelly上面提到的SO帖子中引用的博客post,并提出了这个解决方案:
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 。