Могу ли я использовать закрытие на обработчике событий (т.е. TButton OnClick)

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

Несовместимые типы: "указатель метода и регулярная процедура"

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

например:

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

Спасибо!

Ответы

Ответ 1

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

end )^ ) + $0C)^;

работает в Delphi 2010

Ответ 2

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

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

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

1: Разрешить создание анонимных методов. Что-то вроде этого:

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

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

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

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

В моих глазах это лучшее решение.

Ответ 3

В предыдущих версиях 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;

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

Ответ 4

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

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

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.