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

Поскольку в 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 имеет очень общие сигнатуры событий, и для каждого особого случая, когда я создаю класс, это делается навсегда.

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

Ответы [ 3 ]

9 голосов
/ 06 ноября 2011

Вы можете взглянуть на мою реализацию многоадресного события в 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;
1 голос
/ 06 ноября 2011

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

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

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

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

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

1 голос
/ 06 ноября 2011

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

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

...