Подтвердить что ты не робот

TProc <TObject> для TNotifyEvent

В дополнение к этому сообщению принятый ответ остается очень загадочным:

@Button1.OnClick := pPointer(Cardinal(pPointer( procedure (sender: tObject) begin ((sender as TButton).Owner as TForm).Caption := 'Freedom to anonymous methods!' end )^ ) + $0C)^;

Интересно, можно ли придумать простейший и элегантный способ, похожий на:

Button.OnClick :=
                    AnonProc2NotifyEvent (
                    procedure (Sender: TObject)
                    begin
                      ((Sender as TButton).Owner as TForm).Caption := 'Freedom to anonymous methods!'
                    end
                      );

чтобы достичь той же цели и где AnonProc2NotifyEvent - это метод владельца Button со следующей подписью:

TOwnerOfButton = class(TForm)
  Button: TButton;
  ...
private
  ...
protected
  function AnonProc2NotifyEvent(aProc: TProc<TObject>): TNotifyEvent;
public
  ...
end;

Возможно ли это, и если да, то как его реализовать?

4b9b3361

Ответ 1

Это сделает работу достаточно легко:

type
  TNotifyEventWrapper = class(TComponent)
  private
    FProc: TProc<TObject>;
  public
    constructor Create(Owner: TComponent; Proc: TProc<TObject>);
  published
    procedure Event(Sender: TObject);
  end;

constructor TNotifyEventWrapper.Create(Owner: TComponent; Proc: TProc<TObject>);
begin
  inherited Create(Owner);
  FProc := Proc;
end;

procedure TNotifyEventWrapper.Event(Sender: TObject);
begin
  FProc(Sender);
end;

function AnonProc2NotifyEvent(Owner: TComponent; Proc: TProc<TObject>): TNotifyEvent;
begin
  Result := TNotifyEventWrapper.Create(Owner, Proc).Event;
end;

Параметр Owner в AnonProc2NotifyEvent заключается в том, что можно управлять временем жизни объекта-обертки. Без чего-то подобного вы могли бы утечка экземпляров TNotifyEventWrapper.

Передайте как Owner, компонент, к которому вы подключаете событие. Например:

Button1.OnClick := AnonProc2NotifyEvent(
  Button1,
  procedure(Sender: TObject)
  begin
    (Sender as TButton).Caption := 'Clicked';
  end
);

Итак, когда кнопка будет уничтожена, TNotifyEventWrapper также будет уничтожен. Объект-оболочка должен проживать как минимум до тех пор, пока объект, к которому он связан, связан. И поэтому выбор Button1 как владельца является естественным и очевидным.

Ответ 2

Для справки, что я получаю, я изучил блог Барри Келли post, упомянутого выше в предыдущей публикации SO, и придумал это решение:

function TMainForm.Proc2NotifyEvent(const aProc: TNotifyReference): TNotifyEvent;
type
  TVtable = array[0..3] of Pointer;
  PVtable = ^TVtable;
  PPVtable = ^PVtable;
begin
  TMethod(Result).Code := PPVtable((@aProc)^)^^[3];
  TMethod(Result).Data := Pointer((@aProc)^);
end;

Все еще загадочно, но инкапсулировано, что облегчает задачу кодера по сравнению с исходным методом.

Я попытался приукрасить MethRefToMethPtr и MakeNotify и поместить все в один метод.

Обратите внимание, что в сигнатуре метода было (небольшое) изменение, аргумент aProc стал const.