AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren

Events in einer Liste: TEventList

Ein Thema von igel457 · begonnen am 19. Sep 2006 · letzter Beitrag vom 19. Sep 2006
Antwort Antwort
Benutzerbild von igel457
igel457

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

Events in einer Liste: TEventList

  Alt 19. Sep 2006, 18:24
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
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
  Mit Zitat antworten Zitat
bttb930

Registriert seit: 6. Okt 2003
372 Beiträge
 
#2

Re: Events in einer Liste: TEventList

  Alt 19. Sep 2006, 18:32
Zitat von igel457:
Manchmal möchte man beliebig viele Eventhandler mit einem Event verknüpfen.
Wollte ich noch nie... kannst Du ein Beispiel nennen?
  Mit Zitat antworten Zitat
Benutzerbild von igel457
igel457

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

Re: Events in einer Liste: TEventList

  Alt 19. Sep 2006, 18:48
Hm...

Du hast recht, meistens gibt es einen anderen, objektorientierten Ansatz mit dem das Ganze besser funktioniert. In meinem Fall habe ich jedoch folgendes Problem:

Ich habe eine Prozedur mit der ich Delphi Objekte auf den Stack der Scriptsprache Lua schieben kann (PushObject). Dazu verwende ich die RTTI. Allerdings habe ich ein Problem mit dem Pushen von Methoden, da ich die Parameter über RTTI nicht herausfinden kann (wenn ich mich irre sagt es mir, wäre nämlich cool). Allerdings möchte ich auch TObject Pushen können, weshalb ein objektorentierter Ansatz (über Vererbung) ausscheidet. (Ich könnte schließlich alle Objekte die ich Pushen will von einer gemeinsamen Basisklasse ableiten, die eine Prozedur hat, die ihre Prozeduren auf dem Stack schiebt.)

Nun habe ich mir überlegt, dass meine "PushObject" Methode ein Event auslöst ("OnPushObject"). So kann ich einfach eine Unit schreiben, die sich mit diesem Event verknüpft und demnach Prozeduren der Objekte auf den Stack schiebt.
Allerdings sind es mittlerweile so viele Objekte die ich Pushen will, sodass ich sie nach Themenbereichen aufteilen will. Die Game-Settings-Lua Wrapper kommt in die "Settings.pas", der Wrapper für meine "DXComponents" kommt in die "DXLua.pas". Deshalb benötige ich mehr als einen Eventhandler.

Puh. Ich hoffe das war verständlich (mein Standardsatz nach solch langen Beiträgen).

Igel457
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
  Mit Zitat antworten Zitat
Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 01:27 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz