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

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

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

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

например:

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

Спасибо!

11.12.2008 17:49:24
4 ОТВЕТА
РЕШЕНИЕ
@Button1.OnClick := pPointer(Cardinal(pPointer( procedure (sender: tObject) 
begin 
  ((sender as TButton).Owner as TForm).Caption := 'Freedom to anonymous methods!' 

end )^ ) + $0C)^;

работает в Delphi 2010

10
19.08.2013 11:00:33
@Majin: это умно, и это работает, потому что анонимный метод встроен в неявный класс. Обязательно прочитайте комментарий Барри Келли в ссылочной статье о будущей совместимости, а также этот пост Барри Келли: blog.barrkel.com/2010/01/using-anonymous-methods-in-method.html
Jeroen Wiert Pluimers 13.04.2011 14:25:19
Это решение работает на платформе Win32, но не на Win64.
Chau Chee Yang 2.07.2014 06:02:04

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

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

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

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

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

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

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

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

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

5
23.12.2008 11:45:54
Метод не имеет той же сигнатуры вызова, что и процедура с теми же аргументами. Методы всегда передают Self как «скрытый» аргумент.
Mason Wheeler 25.12.2008 17:48:42
Ну конечно; естественно. Но я не вижу причин, по которым компилятор не может обрабатывать оба случая "за сценой". Например, он может создать фиктивную оболочку класса вокруг анонимной процедуры, если ожидается метод.
Hans-Eric 3.02.2009 08:29:46
Этот шаблон так распространен в динамических языках, таких как JavaScript Python.
Edwin Yip 22.10.2013 08:41:27

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

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

4
4.01.2009 09:35:39
Я только что проверил это, и это не сработало с замыканием, но я мог что-то упустить.
Jim McKeeth 4.01.2009 10:25:54

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

Применение

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.
2
18.12.2015 16:35:28