События 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 имеет очень распространенные сигнатуры событий и для каждого отдельного случая, когда я создаю класс, который он выполнял навсегда.
Что вы думаете об этой реализации? Что-то сделать лучше?
Ответы
Ответ 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. Затем подсчет ссылок должен заботиться об уничтожении экземпляра, когда форма больше не используется. Обновить: это требует хранения ссылки на экземпляр где-то в форме, или пересчет не поможет, поскольку экземпляр будет освобожден сразу после назначения события уведомления. Вы можете добавить дополнительный параметр в класс Создать функции, которым вы передаете метод, который экземпляр может использовать для добавления в какой-либо список формы.
Боковое примечание: В целом, хотя я должен согласиться с Дэвидом в его комментарии к вопросу: это звучит как большая работа для "единственной цели" использования анонимных методов..