Ответ 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
Если я пытаюсь использовать замыкание на обработчике событий, компилятор жалуется на:
Несовместимые типы: "указатель метода и регулярная процедура"
который я понимаю.. но есть ли способ использовать кузнец на указателях метода? и как определить, может ли?
например:
Button1.Onclick = procedure( sender : tobject ) begin ... end;
Спасибо!
@Button1.OnClick := pPointer(Cardinal(pPointer( procedure (sender: tObject)
begin
((sender as TButton).Owner as TForm).Caption := 'Freedom to anonymous methods!'
end )^ ) + $0C)^;
работает в Delphi 2010
Отличный вопрос.
Насколько я знаю, это невозможно сделать в текущей версии Delphi. Это очень печально, так как эти анонимные процедуры были бы полезны для быстрой настройки обработчиков событий объекта, например, при настройке тестовых устройств в инфраструктуре автоматического тестирования xUnit.
Для CodeGear должно быть два способа реализовать эту функцию:
1: Разрешить создание анонимных методов. Что-то вроде этого:
Button1.OnClick := procedure( sender : tobject ) of object begin
...
end;
Проблема заключается в том, что поставить в качестве самостоятельного указателя для анонимного метода. Можно было бы использовать указатель на объект объекта, из которого был создан анонимный метод, но затем можно создавать анонимные методы из контекста объекта. Лучшей идеей может быть просто создание фиктивного объекта за кулисами, чтобы содержать анонимный метод.
2: В качестве альтернативы можно позволить типам событий принимать как методы, так и процедуры, если они разделяют определенную подпись. Таким образом, вы можете создать обработчик событий так, как вы хотите:
Button1.OnClick := procedure( sender : tobject ) begin
...
end;
В моих глазах это лучшее решение.
В предыдущих версиях 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;
Я не уверен, что это действительно компилируется, но это должно дать вам общую идею. Я сделал это раньше, и он работал для регулярных процедур. Поскольку я не знаю, какой код генерирует компилятор для закрытия, я не могу сказать, будет ли это работать для них.
Его легко расширить, чтобы обрабатывать больше типов событий формы.
Использование
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.