While playing a bit with Simon Stuarts LKSL event engine, I thought it would be quite handy to have a generic TLKEventListener taking away the need for some boiler plate code. Here it is:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
type TLKEventListener<T: TLKEvent> = class(TLKEventListener) type TEventCallback = procedure(const AEvent: T) of object; private FOnEvent: TEventCallback; function GetOnEvent: TEventCallback; procedure SetOnEvent(const AOnEvent: TEventCallback); public procedure EventCall(const AEvent: TLKEvent); override; function GetEventType: TLKEventType; override; property OnEvent: TEventCallback read GetOnEvent write SetOnEvent; end; procedure TLKEventListener<T>.EventCall(const AEvent: TLKEvent); var callback: TEventCallback; begin inherited; callback := OnEvent; if Assigned(callback) then callback(T(AEvent)); end; function TLKEventListener<T>.GetEventType: TLKEventType; begin // We MUST return the exact Event Type this Listener is intended to listen for. Result := T; end; function TLKEventListener<T>.GetOnEvent: TEventCallback; begin Lock; try Result := FOnEvent; finally Unlock; end; end; procedure TLKEventListener<T>.SetOnEvent(const AOnEvent: TEventCallback); begin Lock; try FOnEvent := AOnEvent; finally Unlock; end; end; |
IMHO you should better keep your lock as small as possible, and use a local variable in the following method:
procedure TLKEventListener<T>.EventCall(const AEvent: TLKEvent);
var callback: TEventCallback;
begin
inherited;
Lock; // We engage the Lock to prevent anything changing the value of “FOnEvent”
try
callback:= FOnEvent;
finally
Unlock; // Must not forget to UNLOCK when we’re done.
end;
if Assigned(callback) then
callback(T(AEvent));
end;
Thanks for the suggestion. As the code is quite similar to that of
GetOnEvent
, I implemented it slightly differently.This is particularly important if the callback ends up trying to replace the OnEvent property of that listener in a “deferred manner” (I.E. from a different thread)… otherwise that Callback will enter an effective deadlock with the Listener.
Hi Uwe,
Would you object to me integrating this into the LKSL itself (obviously crediting you for the code and placing a link to your site here in the comment block)?
No problem. Go ahead.
It would also be very nice to have a bridge to http://docwiki.embarcadero.com/Libraries/de/System.Messaging
There are a lot of useful messages you can receive from there, but it is very unhandy to have to deal with several different messengers.
For me I have build an extended version of the TMessageMangager (TMessageManagerEx) using generics, synchronize, etc. that will seamlessly integrate the standard TMessageManager.
The LKSL Event Engine was built SPECIFICALLY to work with its special “High Precision Event Threads” as a means of producing asynchronous, multi-threaded applications (where information, which in a sense acts as an instruction, is communicated between threads using the Event Engine).
It’s not intended as a UI messaging system, or to “compete” with any of Delphi’s internal features… merely as a critical part of a new development methodology.
Also, it was designed originally for my game engine… but once I realized it had a variety of potential purposes, I decided to put it out there as a part of my standard library.
For the benefit of future readers, I have syndicated this awesome little feature into the LKSL repository 🙂