Ich habe den
Originalbeitrag mal etwas überarbeitet, da ich im nachhinein noch Fehler fand und ich mal noch ein Beispiel eingefügt habe.
Wie kann man Events in einer Liste speichern?
Manchmal möchte man beliebig viele Eventhandler mit einem Event verknüpfen. Wem jetzt konkret kein Beispiel einfällt, der denke an folgende Konstellation:
Um bei einer Grafikengine die Auflösung ändern zu können muss man meistens die gesammte Engine "Runterfahren" und dann wieder "Hochfahren", was das komplette freigeben und wieder erstellen der Engine zur folge hat. Problem ist, dass dabei auch alle Texturdaten verloren gehen. Dabei wäre es doch praktisch wenn beim Finalisieren automatisch alle Texturobjekte dies mitbekommen und dabei noch ihre Daten sichern können und beim Initialisieren diese wieder in die Engine "hineinschieben".
Dies
kann mann über eine so genannte "Event-Liste" lösen.
Allerdings müssen wir zwei Fälle unterscheiden:
- Eine Liste mit Methodenzeigern
- Eine Liste mit Prozedurzeigern
Diese Unterscheidung ist aus folgendem Grund nötig: Ein Methodenzeiger zeigt auf eine Prozedur in einem Objekt. Würde man einen Methodenzeiger wie einen Prozedurzeiger behandeln so geht die Referenz auf das Objekt verloren, da der Methodenzeiger wie ein Record aufgebaut ist. Das bedeutet das in der aufgerufenen Methode vermutlich eine
Access-Violation auftritt, da "self" "nil" ist.
Beginnen wir zunächst mit dem einfacheren, der Liste mit Prozedurzeigern:
Eigentlich ist das Unterfangen ganz einfach. Man muss jedoch beachten, nur die Adressen auf die Methoden in der Liste zu speichern.
Das Interface schaut folgendermaßen aus:
Delphi-Quellcode:
type
TMyEvent = procedure; //Die Prozedur darf frei herum stehen --> Einfacher Prozedur-, Funktionzeiger
TMyEventList = class(TList)
private
function GetItem(AIndex:integer):TMyEvent;
procedure SetItem(AIndex:integer;AItem:TMyEvent);
protected
public
property Items[AIndex:integer]:TMyEvent read GetItem write SetItem;default;
end;
Sieht eigentlich wie eine normale typensichere Liste aus.
Die Implementierung:
Delphi-Quellcode:
function TMyEventList.GetItem(AIndex:integer):TMyEvent;
begin
@result := inherited Items[AIndex];
end;
procedure TMyEventList.SetItem(AIndex:integer;AItem:TMyEvent);
begin
inherited Items[AIndex] := @AItem;
end;
Wie man sieht, wird jeweils nur die Adresse der Methode in der Liste gespeichert.
Einfügen von Events in die Liste:
EventList.Add(@MyEventHandler);
Aufrufen von Events aus der Liste:
EventList.Items[i](ggf, die, Parameter);
Schwieriger wird es mit der Liste auf Methodenzeiger:
Erst das Interface:
Delphi-Quellcode:
type
TMyEvent = procedure of object; //Die Prozedur ist Teil eines Objekts --> Methodenzeiger
PMyEvent = ^TMyEvent;
TMyEventList = class(TList)
private
function GetItem(AIndex:integer):TMyEvent;
procedure SetItem(AIndex:integer;AItem:TMyEvent);
protected
procedure Notify(Ptr: Pointer; Action: TListNotification);override;
public
property Items[AIndex:integer]:TMyEvent read GetItem write SetItem;default;
procedure Add(Item:TMyEvent);
procedure Remove(Item:TMyEvent);
end;
Und jetzt die Implementierung:
Delphi-Quellcode:
{ TMyEventList }
procedure TMyEventList.Add(Item: TMyEvent);
var Event:PMyEvent;
begin
New(Event);
Event^ := Item;
inherited Add(Event);
end;
function TMyEventList.GetItem(AIndex:integer):TMyEvent;
begin
result := PSurfaceEvent(inherited Items[AIndex])^;
end;
procedure TMyEventList.Notify(Ptr: Pointer; Action: TListNotification);
begin
if Action = lnDeleted then
begin
Dispose(Ptr);
end;
inherited;
end;
procedure TMyEventList.Remove(Item: TMyEvent);
var i:integer;
begin
i := 0;
while i < Count do
begin
if (TMethod(Items[i]).Code = TMethod(Item).Code) and
(TMethod(Items[i]).Data = TMethod(Item).Data) then
begin
Delete(i);
end;
i := i + 1;
end;
end;
procedure TMyEventList.SetItem(AIndex:integer;AItem:TMyEvent);
begin
inherited Items[AIndex] := @AItem;
end;
Hier reicht es nich einfach nur den Pointer der Liste hinzuzufügen, da der Methodenzeiger eigentlich wie ein Record aufgebaut ist.
Das hinzüfungen von Elementen geschieht folgendermaßen:
EventList.Add(Event);
Und das aufrufen:
EventList.Items[i](ggf,die,parameter);
Wie immer viel Spaß mit dem Code!
igel457
Suchbegriffe: Eventlist, Events in Liste, TNotifyEvent, TEventlist, Eventlist, Methodenzeiger
[edit=Luckie] Mfg, Luckie[/edit]