Могу ли я использовать замыкание в обработчике событий (например, TButton OnClick) - PullRequest
14 голосов
/ 11 декабря 2008

Если я пытаюсь использовать замыкание в обработчике событий, компилятор жалуется:

Несовместимые типы: «указатель метода и обычная процедура»

что я понимаю .. но есть ли способ использовать clouser для указателей на метод? а как определить можно ли?

Например:

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

Спасибо!

Ответы [ 4 ]

10 голосов
/ 13 апреля 2011
@Button1.OnClick := pPointer(Cardinal(pPointer( procedure (sender: tObject) 
begin 
  ((sender as TButton).Owner as TForm).Caption := 'Freedom to anonymous methods!' 

end )^ ) + $0C)^;

работает в Delphi 2010

5 голосов
/ 23 декабря 2008

Отличный вопрос.

Насколько я знаю, это невозможно сделать в текущей версии Delphi. Это очень прискорбно, поскольку эти анонимные процедуры были бы полезны для быстрой настройки обработчиков событий объекта, например, при настройке тестовых устройств в среде автоматического тестирования типа xUnit.

У CodeGear должно быть два способа реализации этой функции:

1: разрешить создание анонимных методов. Примерно так:

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

Проблема в том, что указывать в качестве собственного указателя для анонимного метода. Можно использовать собственный указатель объекта, из которого был создан анонимный метод, но тогда можно создавать только анонимные методы из контекста объекта. Лучше было бы просто создать закулисный объект за кулисами для анонимного метода.

2: В качестве альтернативы можно разрешить типам событий принимать как методы, так и процедуры, если они совместно используют определенную подпись. Таким образом, вы можете создать обработчик событий так, как вам нужно:

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

На мой взгляд, это лучшее решение.

4 голосов
/ 04 января 2009

В предыдущих версиях Delphi вы могли использовать обычную процедуру в качестве обработчика событий, добавив скрытый указатель self на параметры и жестко введите его:

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;

Я не уверен, что вышесказанное действительно компилируется, но оно должно дать вам общее представление. Я делал это ранее, и это работало для регулярных процедур. Поскольку я не знаю, какой код генерирует компилятор для замыканий, я не могу сказать, будет ли это работать для них.

2 голосов
/ 18 декабря 2015

Это легко расширить ниже, чтобы обрабатывать больше типов событий формы.

Использование

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.
...