19

除了这篇文章,其接受的答案仍然非常神秘:

@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;

这是否可行,如果可行,如何实施?

4

2 回答 2

39

这将很容易完成这项工作:

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参数 inAnonProc2NotifyEvent是为了管理包装对象的生命周期。如果没有类似的东西,您将泄漏TNotifyEventWrapper.

传递 as Owner,您要将事件连接到的组件。例如:

Button1.OnClick := AnonProc2NotifyEvent(
  Button1,
  procedure(Sender: TObject)
  begin
    (Sender as TButton).Caption := 'Clicked';
  end
);

所以,当按钮被销毁时,TNotifyEventWrapper也会被销毁。包装对象必须至少与它关联的事件的对象一样长。因此,Button1作为所有者的选择是自然而然的选择。

于 2012-07-15T12:11:19.013 回答
5

作为参考,我研究了上面提到的之前 SO 帖子中引用的Barry Kelly的博客文章,并提出了这个解决方案:

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;

仍然神秘但被封装,因此与初始方法相比,简化了编码器的任务。

我试图整理MethRefToMethPtrMakeNotify并将它们全部放在一个方法中。

请注意,方法的签名有(轻微的)变化,参数aProc变为const

于 2012-07-15T14:00:10.547 回答