Hi,
ich habe ein Problem mit Delphi 2009.
Ich will einen Multicast delegate für einen TButton-Click bauen (beispielhaft, soll gegen später generisch mit jedem Event gehen).
Die Idee ist folgende:
Mittels eines Class helpers erweitere ich die Klasse TButton um zwei Methoden: AddEvent und RemoveEvent.
In einem generischen Dictionary merke ich mir dann zu jedem verwendeten Button eine Liste mit registrierten Events. Wird mit AddEvent eines hinzugefügt, so hänge ich dieses Event in die Liste ein und bei remove soll das Event auch aus der Liste entfernt werden. (Ja, ich fake mittels des Dictionarys sozusagen ein Property zu dem Button dazu - class helper mit zusätzlichen Properties könnten mittels eines generischen Dictionaries auch so hinzubekommen sein).
Das Hinzufügen und Ausführen der Events funktioniert auch schon, aber das Entfernen eines einmal registrierten Events schmeisst eine Zugriffsverletzung, und zwar genau an der Stelle an dem geprüft wird, ob das Event in der Liste ist.
Kann man jemand drübergucken und mir sagen, warum ich das Event auf diese Art zwar in die Liste hinzufügen, aber nicht mer entfernen kann? Mir erscheint das etwas unlogisch und die Zugriffsverletzung passiert jedes mal an einer anderen Adresse, so dass ich gar nicht sagen kann wo die herkommt.
Hier mal die komplette betreffende
Unit:
Delphi-Quellcode:
unit UMulticaster;
interface
uses
Classes, StdCtrls;
type
TButtonMulticaster =
class helper
for TButton
procedure AddEvent(event: TNotifyEvent);
overload;
procedure RemoveEvent(event: TNotifyEvent);
overload;
end;
implementation
uses
SysUtils, Generics.Defaults, Generics.Collections;
type
eventList = TList<TNotifyEvent>;
eventDictionary = TDictionary<TButton, eventList>;
EventDispatcher =
class
private
eventList: eventDictionary;
public
constructor Create;
destructor Destroy;
public
procedure OnSomeButtonClick(sender: TObject);
property Events: eventDictionary
read eventList
write eventList;
end;
var
dispatcher: EventDispatcher;
{ EventDispatcher }
constructor EventDispatcher.Create;
begin
eventList := eventDictionary.Create();
end;
destructor EventDispatcher.Destroy;
begin
FreeAndNil(eventList);
end;
procedure EventDispatcher.OnSomeButtonClick(sender: TObject);
var
event: TNotifyEvent;
begin
for event
in eventList.Items[TButton(sender)]
do
begin
event(sender);
end;
end;
{ TButtonMulticaster }
procedure TButtonMulticaster.AddEvent(event: TNotifyEvent);
begin
if not dispatcher.Events.ContainsKey(self)
then
begin
dispatcher.Events.Add(self, eventList.Create());
self.OnClick := dispatcher.OnSomeButtonClick;
end;
dispatcher.Events[self].Add(event);
end;
procedure TButtonMulticaster.RemoveEvent(event: TNotifyEvent);
var
evList: eventList;
begin
if dispatcher.Events.ContainsKey(self)
then
begin
evList := dispatcher.Events[self];
if evList.IndexOf(event) >= 0
then // HIER wird die Zugriffsverletzung ausgelöst
evList.Remove(event);
if dispatcher.Events[self].Count = 0
then
begin
dispatcher.Events.Remove(self);
self.OnClick :=
nil;
end;
end;
end;
initialization
begin
dispatcher := EventDispatcher.Create();
end;
finalization
begin
FreeAndNil(dispatcher);
end;
end.