我可以在事件处理程序上使用闭包(即TButton OnClick)

时间:2008-12-11 17:49:25

标签: delphi delphi-2009

如果我尝试在事件处理程序上使用闭包,编译器会抱怨:

不兼容的类型:“方法指针和常规过程”

我理解..但有没有办法在方法指针上使用clouser?以及如何定义是否可以?

例如:

Button1.Onclick = procedure( sender : tobject ) begin ... end;

谢谢!

4 个答案:

答案 0 :(得分:10)

@Button1.OnClick := pPointer(Cardinal(pPointer( procedure (sender: tObject) 
begin 
  ((sender as TButton).Owner as TForm).Caption := 'Freedom to anonymous methods!' 

end )^ ) + $0C)^;
Delphi 2010中的

works

答案 1 :(得分:5)

一个很好的问题。

据我所知,在当前版本的Delphi中无法做到。这非常不幸,因为这些匿名过程对于快速设置对象的事件处理程序非常有用,例如在xUnit类型的自动测试框架中设置测试夹具时。

CodeGear应该有两种方式来实现此功能:

1:允许创建匿名方法。像这样:

Button1.OnClick := procedure( sender : tobject ) of object begin
  ...
end;

这里的问题是作为匿名方法的自指针放置的内容。可以使用创建匿名方法的对象的self指针,但是只能从对象上下文创建匿名方法。更好的想法可能是在幕后创建一个虚拟对象以包含匿名方法。

2:或者,可以允许事件类型接受方法和过程,只要它们共享定义的签名即可。通过这种方式,您可以按照自己的方式创建事件处理程序:

Button1.OnClick := procedure( sender : tobject ) begin
  ...
end;

在我看来,这是最好的解决方案。

答案 2 :(得分:4)

在以前的Delphi版本中,您可以使用常规过程作为事件处理程序,方法是将隐藏的自我指针添加到参数中并对其进行强制转换:

procedure MyFakeMethod(_self: pointer; _Sender: TObject);
begin
  // do not access _self here! It is not valid
  ...
end;

...

var
  Meth: TMethod;
begin
  Meth.Data := nil;
  Meth.Code := @MyFakeMethod;
  Button1.OnClick := TNotifyEvent(Meth);
end;

我不确定上面的内容确实可以编译,但它应该给你一般的想法。我以前做过这个,它适用于常规程序。由于我不知道编译器为闭包生成了什么代码,我不能说这是否适用于它们。

答案 3 :(得分:2)

很容易扩展下面的内容以处理更多表单事件类型。

用法

procedure TForm36.Button2Click(Sender: TObject);
var
  Win: TForm;
begin
  Win:= TForm.Create(Self);
  Win.OnClick:= TEventComponent.NotifyEvent(Win, procedure begin ShowMessage('Hello'); Win.Free; end);
  Win.Show;
end;

代码

unit AnonEvents;

interface
uses
  SysUtils, Classes;

type
  TEventComponent = class(TComponent)
  protected
    FAnon: TProc;
    procedure Notify(Sender: TObject);
    class function MakeComponent(const AOwner: TComponent; const AProc: TProc): TEventComponent;
  public
    class function NotifyEvent(const AOwner: TComponent; const AProc: TProc): TNotifyEvent;
  end;

implementation

{ TEventComponent }

class function TEventComponent.MakeComponent(const AOwner: TComponent;
  const AProc: TProc): TEventComponent;
begin
  Result:= TEventComponent.Create(AOwner);
  Result.FAnon:= AProc;
end;

procedure TEventComponent.Notify(Sender: TObject);
begin
  FAnon();
end;

class function TEventComponent.NotifyEvent(const AOwner: TComponent;
  const AProc: TProc): TNotifyEvent;
begin
  Result:= MakeComponent(AOwner, AProc).Notify;
end;

end.