.
Warum MultiEvents?
Die nativen Events in Delphi haben zwei große Nachteile:
- Es ist eine 1 zu 1 Verbindung, es gibt ein Event-Auslöser (trigger) und den Event-Zuhörer (event listener or handler)
jedoch ist es oftmals gewünscht an mehreren Stellen auf ein Event zu reagieren.
Die MultiEvents führen hier die aus vielen anderen Programmiersprachen (z.B. Java) bekannte 1 zu N Verbingdung ein,
in der sich zu einem Event X mehrere Zuhörer über die Add() Methode registrieren können.
- Die Events sind nur innerhalb des Delphi Sprachraums gültig. Für programmiersprachenübergreifende Schnittstellen gibt
es keine "out of the box" Lösung.
Das besondere an den MultiEvents ist die programmiersprachenübergreifende Verträglichkeit. Durch Interfaces und COM
Datentypen ermöglichen dies den Einsatz in großen Plug-In Systemen.
Außerdem nutzen die MultiEvents geschickt die in D2009+ eingeführten Generics
um den Mehraufwand an Code auf ein Minimum zu reduzieren.
Inhalt- Interfaces als Ausgangspunkt
- 1 zu 1 Verbindung
- 1 zu N Verbindung
- Einfache Benutzung
- Event-Auslöser Betrachtung
- Event-Zuhörer Betrachtung
- Quellcodeminimierung durch Generics
- TGenericEventHandler Klasse
- TGenericEvent Klasse
- Verweise
1. Interfaces als Ausgangspunkt
1 zu 1 Verbindung
Ganz ohne das alt bekannte
procedure ... of object Gerüst geht es nicht. Zusätzlich wird ein EventHandler Interface definiert.
Delphi-Quellcode:
TNotifyMethod = procedure(const Sender: IUnknown) of object;
INotifyEventHandler = interface(IUnknown)
['{EE9407DD-2337-4DFC-BC35-A40C4FA0A1A7}']
procedure Invoke(const Sender: IUnknown); safecall;
end;
Aus diesen beiden "Bauteilen" entsteht die
TINotifyEventHandler Klasse, die das zuvor definierte Interface implementiert. Dies ist eine Art Wrapper für die
TNotifyMethod.
Delphi-Quellcode:
TINotifyEventHandler = class(TGenericEventHandler<TNotifyMethod>, INotifyEventHandler)
public
procedure Invoke(const Sender: IUnknown); safecall;
end;
1 zu N Verbindung
Hat man erstmal einen EventHandler definiert kann man daraus nun ein entsprechendes MultiEvent erstellen. Über
Add,
Remove können dann Instanzen des
INotifyEventHandlers hinzugefügt und wieder entfernt werden. Mit
Invoke ruft man das Event auf. Somit werden alle Events die in dem MultiEvent registriert sind benachrichtigt.
Delphi-Quellcode:
INotifyEvent = interface(IUnknown)
['{14551B63-78C4-4A70-9E54-7656CEF4D6A7}']
procedure Add(const AHandler: INotifyEventHandler); safecall;
procedure Remove(const AHandler: INotifyEventHandler); safecall;
procedure Invoke(const Sender: IUnknown); safecall;
end;
Für die Implementierung reicht es eine for-Schleife in der
Invoke-Methode zu tippen:
Delphi-Quellcode:
TINotifyEvent = class(TGenericEvent<INotifyEventHandler>, INotifyEvent)
public
procedure Invoke(const Sender: IUnknown); safecall;
end;
2. Einfache Benutzung
Event-Auslöser Betrachtung
In der Klasse in der ein MultiEvent eingesetzt werden soll wird eine lokale Variable
FNotifyEvent: INotifyEvent;
definiert. Außerdem muss für den Zugriff von außen gesorgt werden - am Besten durch eine property und einen getter.
Die Erstellung, ein Aufruf und die Zerstörung ist so einfach wie es nur sein kann:
Delphi-Quellcode:
// Im constructor der Klasse
FNotifyEvent := TINotifyEvent.Create;
// button1 von Typ TButton
// Eine Event Nachricht/Aufruf sieht das bspw. so aus
NotifyEvent.Invoke(button1);
// Im destructor der Klasse
FNotifyEvent := nil;
Event-Zuhörer Betrachtung
Die Klasse die sich beim MultiEvent registiert. Wie schon erwähnt benötigt diese Zugriff auf das MultiEvent (hier:
Main.NotifyEvent).
Delphi-Quellcode:
TOtherForm = class(TForm)
// [...]
private
FNotifyEventHandler: INotifyEventHandler;
protected
// Diese Methode wird dann aufgerufen
procedure OnNotify(const Sender: IUnknown);
// [...]
// Im constructor der Klasse
FNotifyEventHandler := TINotifyEventHandler.Create(OnNotify);
Main.NotifyEvent.Add(FNotifyEventHandler);
// Im destructor der Klasse
Main.NotifyEvent.Remove(FNotifyEventHandler);
FNotifyEventHandler := nil;
3. Quellcodeminimierung durch Generics
TGenericEventHandler Klasse
Delphi-Quellcode:
TGenericEventHandler<T: constructor> = class abstract(TInterfacedObject)
protected
FHandler: T;
public
constructor Create(AEventHandler: T);
property EventHandler: T read FHandler write FHandler;
destructor Destroy; override;
end;
TGenericEvent Klasse
Delphi-Quellcode:
TGenericEvent<T: IUnknown> = class abstract(TInterfacedObject)
private
FMethods: TList<T>;
protected
property Methods: TList<T>read FMethods;
public
constructor Create; virtual;
procedure Add(const AHandler: T); safecall;
procedure Remove(const AHandler: T); safecall;
destructor Destroy; override;
end;
4. Verweise
Die Arbeit von
Jolyon Smith hat mich ich für MultiEvents auf Interface-Basis inspiriert. Da dies mMn noch nicht Dokumentiert ist, sollte es an dieser Stelle ganz gut aufgehoben sein.
Ähnliche Ansätze
http://www.delphipraxis.net/158578-m...st-events.html
Multicast events using generics
Im Anhang befindet sich der komplette Quellcode und ein kleines Beispiel. Die Events lassen sich natürlich nach eigenen Wünschen beliebig anpassen und unter Verwendung der generischen Klassen, sollte sich die Tipparbeit in Grenzen halten.
Viel Spaß damit.
.