问题
Since anonymous methods appeared in Delphi I wanted to use them in VCL components events. Obviously for backward compatibility the VCL wasn't updated, so I managed to make a simple implementation with a few caveats.
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.
And this is how it's used for example:
procedure TForm1.FormCreate(Sender: TObject);
begin
Button1.OnClick := TNotifyEventDispatcher.Create(Self,
procedure (Sender: TObject)
begin
Self.Caption := 'DONE!'
end)
end;
Very simple I believe, there are two drawbacks:
I have to create a component to manage the lifetime of the anonymous method (I waste a bit more of memory, and it's a bit slower for the indirection, still I prefer more clear code in my applications)
I have to implement a new class (very simple) for every event signature. This one is a bit more complicated, still the VCL has very common event signatures, and for every special case when I create the class it's done forever.
What do you think of this implementation? Something to make it better?
回答1:
You can take a look at my multicast event implementation in DSharp.
Then you can write code like this:
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:
You could make TNotifyEventDispatcher
to be a subclass of TInterfacedObject
so you do not need to care about freeing it.
But to be more pragmatic one would use traditional event assignment that takes less lines of code and is supported by the IDE.
回答3:
Interesting approach.
(Disclaimer: haven't checked this, but it is something to investigate): You may have to be careful though about what goes on in capturing the state of the method that "assigns" the anonymous method to the event. Capturing can be an advantage but can also have side effects you do not want. If your anonymous method needs info about the form at the time it is fired, it may end up with info at the time of its assignment. Update: apparently this is not the case, see comment by Stefan Glienke.
You do not really need different classes. Using overloading you could create different class Create
functions that each take a specific signature and return the corresponding event handler and the compiler will sort it out.
Managing lifetime could be simplified if you derived from TInterfacedObject instead of TComponent. Reference counting should then take care of destroying the instance when it is no longer used by the form. Update: this does require keeping a reference to the instance somewhere in the form, or refcounting won't help as the instance will be freed immediately after assigning the notify event. You could add an extra parameter on the class Create functions to which you pass a method that the instance can useto add itself to some list of the form.
Side note: All in all though I have to agree with David in his comment on the question: it does sound like a lot of work for the "sole purpose" of using anonymous methods...
来源:https://stackoverflow.com/questions/8025481/vcl-events-with-anonymous-methods-what-do-you-think-about-this-implementation