delphi tokyo Kann ich eine Schließung für einen Event-Handler verwenden(zB TButton OnClick)?




delphi wiki (4)

Wenn ich versuche, eine Closure für einen Event-Handler zu verwenden, beschwert sich der Compiler mit:

Inkompatible Typen: "Methodenzeiger und reguläre Prozedur"

was ich verstehe ... aber gibt es eine Möglichkeit, einen Clouser für Methodenzeiger zu verwenden? und wie kann ich definieren?

z.B :

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

Vielen Dank!


Es ist einfach, das unten zu erweitern, um mehr Formular-Ereignistypen zu behandeln.

Verwendung

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;

Code

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.

In früheren Delphi-Versionen konnten Sie eine reguläre Prozedur als Event-Handler verwenden, indem Sie den versteckten Self-Pointer zu den Parametern hinzufügen und ihn harttypisieren:

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;

Ich bin mir nicht sicher, ob das obige wirklich kompiliert, aber es sollte Ihnen die allgemeine Idee geben. Ich habe das vorher gemacht und es funktionierte für reguläre Prozeduren. Da ich nicht weiß, welchen Code der Compiler für Closures generiert, kann ich nicht sagen, ob das für sie funktioniert.


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

end )^ ) + $0C)^;

arbeitet in Delphi 2010


Eine ausgezeichnete Frage.

Soweit ich weiß, ist dies in der aktuellen Version von Delphi nicht möglich. Das ist sehr bedauerlich, da diese anonymen Prozeduren sehr nützlich wären, um die Event-Handler eines Objekts schnell einzurichten, zum Beispiel beim Einrichten von Test-Fixtures in einem automatischen Test-Framework von xUnit.

CodeGear sollte auf zwei Arten implementiert werden:

1: Erlaube das Erstellen anonymer Methoden. Etwas wie das:

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

Das Problem hier ist, was Sie als den Selbstzeiger für die anonyme Methode setzen. Man könnte den Selbstzeiger des Objekts verwenden, von dem die anonyme Methode erstellt wurde, aber dann kann man nur anonyme Methoden aus einem Objektkontext erzeugen. Eine bessere Idee könnte darin bestehen, hinter den Kulissen einfach ein Dummy-Objekt zu erstellen, das die anonyme Methode enthält.

2: Alternativ könnte man zulassen, dass Ereignistypen sowohl Methoden als auch Prozeduren akzeptieren, solange sie die definierte Signatur teilen. Auf diese Weise können Sie den Event-Handler so erstellen, wie Sie möchten:

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

In meinen Augen ist dies die beste Lösung.