【问题标题】:TProc<TObject> to TNotifyEventTProc<TObject> 到 TNotifyEvent
【发布时间】:2012-07-14 13:12:12
【问题描述】:

除此之外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;

这可行吗?如果可行,如何实施?

【问题讨论】:

  • 你可能想看看DSharp.Core.Events.pas
  • @Stefan Glienke:谢谢你提醒我,我确实在我的盒子上安装了 DSharp,但我错过了它(我不屑一顾,我不经常使用它)但相信我会关注它自成立以来(顺便说一句,我也是 DelphiPraxis 的成员,德语不是很好,但努力关注蓬勃发展的德国 Delphi 场景)。可以肯定的是,Generics 是我的下一步。谢谢史蒂夫 :-)

标签: delphi delphi-xe anonymous


【解决方案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;

AnonProc2NotifyEvent 中的Owner 参数是为了管理包装对象的生命周期。如果没有类似的东西,您将泄漏TNotifyEventWrapper 的实例。

作为Owner 传递,您要连接事件的组件。例如:

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

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

【讨论】:

  • 谢谢大卫!我从来没有想过包装器会做,它是组件化的好选择:简洁、干净和优雅的解决方案。
  • 这很漂亮。我将此应用于 TNetHttpClient 异步模式
  • 我也是这么想的。漂亮。
【解决方案2】:

作为参考,我研究了Barry Kelly 的博客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;

仍然是神秘的,但被封装了,因此与初始方法相比,编码器的任务更加轻松。

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

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

【讨论】:

  • 您仍然需要做一些事情来使实现 anon proc 接口的对象保持活动状态。巴里确实提到了这个重要的细节。
  • @David Heffernan:我应该在某处保留参考资料吗?
  • 需要有对 anon 方法的引用才能使实现对象保持活动状态
  • 您可以在将匿名方法分配给TMethod.Data 时手动增加引用计数。当组件 (Button1) 不再使用该事件时,您只需要记住减少引用计数,例如:type PInterface = ^IInterface; IInterface(TMethod(Result).Data) := PInterface(@aProc)^; ... IInterface(TMethod(Result).Data) := nil;
猜你喜欢
  • 2011-07-15
  • 1970-01-01
  • 2010-10-16
  • 1970-01-01
  • 1970-01-01
  • 2011-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多