Einzelnen Beitrag anzeigen

Benutzerbild von igel457
igel457

Registriert seit: 31. Aug 2005
1.622 Beiträge
 
FreePascal / Lazarus
 
#1

Ergänzung zu "Events in einer Liste"

  Alt 30. Dez 2006, 12:52
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]
Andreas
"Sollen sich auch alle schämen, die gedankenlos sich der Wunder der Wissenschaft und Technik bedienen, und nicht mehr davon geistig erfasst haben als die Kuh von der Botanik der Pflanzen, die sie mit Wohlbehagen frisst." - Albert Einstein