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

События VCL с анонимными методами - что вы думаете об этой реализации?

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

type
  TNotifyEventDispatcher = class(TComponent)
  protected
    FClosure: TProc<TObject>;

    procedure OnNotifyEvent(Sender: TObject);
  public
    class function Create(Owner: TComponent; Closure: TProc<TObject>): TNotifyEvent; overload;

    function Attach(Closure: TProc<TObject>): TNotifyEvent;
  end;

implementation

class function TNotifyEventDispatcher.Create(Owner: TComponent; Closure: TProc<TObject>): TNotifyEvent;
begin
  Result := TNotifyEventDispatcher.Create(Owner).Attach(Closure)
end;

function TNotifyEventDispatcher.Attach(Closure: TProc<TObject>): TNotifyEvent;
begin
  FClosure := Closure;
  Result := Self.OnNotifyEvent
end;

procedure TNotifyEventDispatcher.OnNotifyEvent(Sender: TObject);
begin
  if Assigned(FClosure) then
    FClosure(Sender)
end;

end.

И вот как он используется, например:

procedure TForm1.FormCreate(Sender: TObject);
begin    
  Button1.OnClick := TNotifyEventDispatcher.Create(Self,
    procedure (Sender: TObject)
    begin
      Self.Caption := 'DONE!'
    end)
end;

Очень просто, я считаю, есть два недостатка:

  • Мне нужно создать компонент для управления временем жизни анонимного метода (я теряю немного больше памяти, и это немного медленнее для косвенности, но я предпочитаю более четкий код в своих приложениях)

  • Мне нужно реализовать новый класс (очень простой) для каждой сигнатуры события. Это немного сложнее, но VCL имеет очень распространенные сигнатуры событий и для каждого отдельного случая, когда я создаю класс, который он выполнял навсегда.

Что вы думаете об этой реализации? Что-то сделать лучше?

4b9b3361

Ответ 1

Вы можете взглянуть на мою реализацию многоадресного события в DSharp.

Затем вы можете написать код следующим образом:

function NotifyEvent(Owner: TComponent; Delegates: array of TProc<TObject>): TNotifyEvent; overload;
begin
  Result := TEventHandler<TNotifyEvent>.Create<TProc<TObject>>(Owner, Delegates).Invoke;
end;

function NotifyEvent(Owner: TComponent; Delegate: TProc<TObject>): TNotifyEvent; overload;
begin
  Result := NotifyEvent(Owner, [Delegate]);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Button1.OnClick := NotifyEvent(Button1, [
    procedure(Sender: TObject)
    begin
      Caption := 'Started';
    end,
    procedure(Sender: TObject)
    begin
      if MessageDlg('Continue?', mtConfirmation, mbYesNo, 0) <> mrYes then
      begin
        Caption := 'Canceled';
        Abort;
      end;
    end,
    procedure(Sender: TObject)
    begin
      Caption := 'Finished';
    end]);
end;

Ответ 2

Вы можете сделать TNotifyEventDispatcher подклассом TInterfacedObject, поэтому вам не нужно заботиться о его освобождении.

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

Ответ 3

Интересный подход.

(Отказ от ответственности: не проверял это, но это что-то для исследования). Возможно, вам придется быть осторожным, хотя о том, что происходит, при захвате состояния метода, который "назначает" анонимный метод событию. Захват может быть преимуществом, но также может иметь побочные эффекты, которые вы не хотите. Если ваш анонимный метод нуждается в информации о форме в момент ее увольнения, он может получить информацию во время ее назначения. Обновление:, очевидно, это не так, см. комментарий Stefan Glienke.

Вам действительно не нужны разные классы. Используя перегрузку, вы можете создавать различные функции class Create, каждая из которых принимает определенную подпись и возвращает соответствующий обработчик событий, а компилятор будет сортировать ее.

Управление временем жизни может быть упрощено, если вы получили от TInterfacedObject вместо TComponent. Затем подсчет ссылок должен заботиться об уничтожении экземпляра, когда форма больше не используется. Обновить: это требует хранения ссылки на экземпляр где-то в форме, или пересчет не поможет, поскольку экземпляр будет освобожден сразу после назначения события уведомления. Вы можете добавить дополнительный параметр в класс Создать функции, которым вы передаете метод, который экземпляр может использовать для добавления в какой-либо список формы.

Боковое примечание: В целом, хотя я должен согласиться с Дэвидом в его комментарии к вопросу: это звучит как большая работа для "единственной цели" использования анонимных методов..