AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Sonstige Fragen zu Delphi Delphi Ergänzung zu "Events in einer Liste"
Thema durchsuchen
Ansicht
Themen-Optionen

Ergänzung zu "Events in einer Liste"

Ein Thema von igel457 · begonnen am 30. Dez 2006 · letzter Beitrag vom 29. Mär 2007
Thema geschlossen
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
 
Der_Unwissende

Registriert seit: 13. Dez 2003
Ort: Berlin
1.756 Beiträge
 
#2

Re: Ergänzung zu "Events in einer Liste&quot;

  Alt 31. Dez 2006, 08:50
Hi,
einen (oder sogar zwei) Wege sollte man vielleicht der Vollständigkeit wegen noch erwähnen, es geht natürlich auch echt objekt orientiert, in dem man statt Methoden-/Funktionszeigern einfach ganze Objekte übergibt. Dazu kann man einfach eine abstrakte Basisklasse mit einer abstrakten Methode erstellen und eben nur Objekte dieser Klasse in die Liste aufnehmen. Da so nun alle Elemente der Liste diese Methode implementieren müssen, kann diese wie eine Callback-Methode behandelt werden. Natürlich geht das dann auch nochmal mit Interfaces.

An sich würde ich auch noch auf das Stichwort Observer-Pattern verweisen wollen (wo Du nebenbei auch noch ganz gute Beispiele für die Verwendung findest und der Name ist vielleicht doch gebräuchlicher als Event-Liste )

Gruß Der Unwissende
 
Benutzerbild von igel457
igel457

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

Re: Ergänzung zu "Events in einer Liste&quot;

  Alt 1. Jan 2007, 15:05
Zitat von Der_Unwissende:
Hi,
einen (oder sogar zwei) Wege sollte man vielleicht der Vollständigkeit wegen noch erwähnen, es geht natürlich auch echt objekt orientiert, in dem man statt Methoden-/Funktionszeigern einfach ganze Objekte übergibt. Dazu kann man einfach eine abstrakte Basisklasse mit einer abstrakten Methode erstellen und eben nur Objekte dieser Klasse in die Liste aufnehmen. Da so nun alle Elemente der Liste diese Methode implementieren müssen, kann diese wie eine Callback-Methode behandelt werden. Natürlich geht das dann auch nochmal mit Interfaces.

An sich würde ich auch noch auf das Stichwort Observer-Pattern verweisen wollen (wo Du nebenbei auch noch ganz gute Beispiele für die Verwendung findest und der Name ist vielleicht doch gebräuchlicher als Event-Liste )

Gruß Der Unwissende
Danke für deine Antwort.
Da hast du schon recht, deshalb habe ich bewust:
Zitat von Igel457:
Dies kann mann über eine so genannte "Event-Liste" lösen.
geschrieben.
Vielleicht sollte ich besser schreiben...
Code:
Dies [u]kann[/u] mann über eine so genannte "Event-Liste" (auch "Observer-Pattern" genannt) lösen, wobei das ganze natürlich auch Objektorientiert funktioniert.
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
 
Benutzerbild von igel457
igel457

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

Re: Ergänzung zu "Events in einer Liste"

  Alt 11. Feb 2007, 12:48
Darf eigentlich ich mal fragen, weshalb dieser Beitrag jetzt schon seit fast 2 Monaten hier "vergammelt" und der mit Fehlern durchlöcherte Beitrag von mir weiterhin in der Codelibrary steht?

[edit]Kleinen Rechtschreibfehler beseitigt[/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
 
Benutzerbild von Shivan
Shivan

Registriert seit: 5. Mär 2003
Ort: Ravensburg
131 Beiträge
 
Delphi 6 Enterprise
 
#5

Re: Ergänzung zu "Events in einer Liste"

  Alt 27. Mär 2007, 18:25
Hi,

wenn ich das richtig erkannt habe, geht die Eventlist hier nur mit einem definierten "Event-Typ". D.h. hier bspw.

TMyEvent = procedure of object; was somit nur mit Events ohne Parameter geht. Wenn ich dann bspw. Events mit einem Parameter Sender: TObject verwalten möchte, muss ich eine neue EventList implementieren, welche auf einen neuen TMyEvent geht welcher bspw als
TMyNewEvent = procedure (Sender: TObject) of object definiert ist. Und dann entsprechend überall TMyEvent durch TMyNewEvent ersetzen?

Oder hab ich da was falsch verstanden? Gibt es keinen Parent in der Ableitungsstruktur, welche alle procedure of object zusammenfasst, egal mit welchen Parametern?
Typprüfung könnte theoretisch doch mit einem Property gemacht werden, welches im Create stattfindet.
.Create(p_MyDefinedEventHandlerType);... Jedoch wüsste ich nicht welchen Datentyp der Parameter haben sollte... Class of TMyEvent oder so ähnlich, was aber nicht geht.

Und im Add prüfen:
Delphi-Quellcode:
procedure TEventList.Add(Item: TMyAllOfEventsParentType);
begin
  if not Item is m_MyDefinedEventHandlerType then
    raise Exception.Create('Falscher Funktionstyp verwendet');
um zu vermeiden, dass falsche Methoden (mit falschen Parameter) in die Liste reinkommen.

Und dann an der Aufrufstelle bequem aufrufen via
(Eventlist.Items[i] as TMyblablaEvent)(param1, param2...);
Markus Stein
 
Benutzerbild von igel457
igel457

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

Re: Ergänzung zu "Events in einer Liste"

  Alt 27. Mär 2007, 18:35
Zitat von Shivan:
Wenn ich dann bspw. Events mit einem Parameter Sender: TObject verwalten möchte, muss ich eine neue EventList implementieren, welche auf einen neuen TMyEvent geht welcher bspw als
TMyNewEvent = procedure (Sender: TObject) of object definiert ist. Und dann entsprechend überall TMyEvent durch TMyNewEvent ersetzen?
Ganz genau. Dein TMyNewEvent wäre hierbei aber equivalent zu TNotifyEvent, welches ja schon (in System.pas denke ich) deklariert ist.

Zitat von Shivan:
Jedoch wüsste ich nicht welchen Datentyp der Parameter haben sollte... Class of TMyEvent oder so ähnlich, was aber nicht geht.
Was du vorschlägst geht aus einem ganz einfachen Grund nicht: TMyEvent oder auch TNotifyEvent ist ein Record des Typs TMethod und keine Klasse. Ich kann mich natürlich auch irren...

Hoffe das hilft dir,
Igel457

PS: Aber kann jetzt vielleicht endlich mal jemand diesen Beitrag mit dem in der CodeLib austauschen? Wäre nett...
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
 
Der_Unwissende

Registriert seit: 13. Dez 2003
Ort: Berlin
1.756 Beiträge
 
#7

Re: Ergänzung zu "Events in einer Liste"

  Alt 27. Mär 2007, 19:12
Zitat von Shivan:
wenn ich das richtig erkannt habe, geht die Eventlist hier nur mit einem definierten "Event-Typ". D.h. hier bspw.

TMyEvent = procedure of object; was somit nur mit Events ohne Parameter geht. Wenn ich dann bspw. Events mit einem Parameter Sender: TObject verwalten möchte, muss ich eine neue EventList implementieren, welche auf einen neuen TMyEvent geht
Hi,
es gibt hier aber ein paar Möglichkeiten das zu umgehen. Wie oben bereits geschrieben kann man das ganze auch mit Interfaces oder Abstrakten Klassen realisieren. Hier würdest Du dann eben ein Interface oder ein Objekt übergeben (also den Verweis darauf), statt den Zeiger auf eine Methode.

Alternativ kannst Du auch noch ein Array of Const als Argument der Methode angeben. Damit wäre es dann möglich, dass Du beliebige Argumente übergibst (müssten Variante Typen sein), allerdings dürfte es das Prüfen auf Korrektheit doch etwas erschweren.

Gruß Der Unwissende
 
Benutzerbild von Shivan
Shivan

Registriert seit: 5. Mär 2003
Ort: Ravensburg
131 Beiträge
 
Delphi 6 Enterprise
 
#8

Re: Ergänzung zu "Events in einer Liste"

  Alt 27. Mär 2007, 23:18
Zitat von igel457:
Was du vorschlägst geht aus einem ganz einfachen Grund nicht: TMyEvent oder auch TNotifyEvent ist ein Record des Typs TMethod und keine Klasse.
Dann wäre ein Schritt, alle TMyEvent's durch TMethod zu ersetzen. Jedoch ob dann die Übergabe der jeweiligen Methoden so klappt? Wahrscheinlich noch hart als TMethod casten im "Add". Werd ich morgen mal probieren. Weil hab eben den Fall, dass ich ein Eventhandling nicht mit Hilfe einer Eventchain lösen möchte, da ich nur an manchen Stellen auf das Event reagieren möchte und mit der klassischen Eventchain beim Entfernen in falscher Reihenfolge ein Problem entstehen wird...

Ich meinte, bei .net oder java oder so gibts doch schon so Eventhandler? Oder lieg ich da falsch... deswegen wollte ich sowas nun auch in Delphi mal verwenden, weil die Idee des Eventhandlers im Gegensatz zur Eventchain eben doch einiges besser ist

Zitat von Der_Unwissende:
...kann man das ganze auch mit Interfaces oder Abstrakten Klassen realisieren. Hier würdest Du dann eben ein Interface oder ein Objekt übergeben (also den Verweis darauf), statt den Zeiger auf eine Methode.
Dennoch müsste ich dort ja ein Property TMethod übergeben, um es verwenden zu können. Oder für jeden Event-Typ eine eigene kleine Klasse, die dann den Aufruf des Ereignisses implementiert, erstellen. Oder hab ich da was falsch verstanden?

Zitat von Der_Unwissende:
Alternativ kannst Du auch noch ein Array of Const als Argument der Methode angeben. Damit wäre es dann möglich, dass Du beliebige Argumente übergibst (müssten Variante Typen sein), allerdings dürfte es das Prüfen auf Korrektheit doch etwas erschweren.
Damit ließen sich dann aber die aktuellen Eventhandler-Methoden nicht so einfach in dem Eventhandler registrieren sondern müssten groß überarbeitet werden.... zumindest hört sich das so an


Mein Ziel war eigentlich sozusagen bspw. ich hab ein TNotifyEvent oder sonstige Events wovon Prozeduren implementiert wurden die im Moment 1:1 auf das Ereignis der jeweiligen Klasse (OnXY) gesetzt werden können... und dort eben statt 1:1 --> 1:n anzulegen
Delphi-Quellcode:
procedure myClass.OnBlubKlick(Sender: TObject);
begin
   //blabla
end;

procedure myClass.OnDingsKlick(Sender: TObject);
begin
  //nochmalwas
end;

procedure myClass.RegisterEventHandlerProcedures;
begin
  m_EventHandler.Register(OnDingsKlick);
  m_EventHandler.Register(OnBlubKlick);
end;

procedure myClass.InvokeEventhandler;
var
  i: Integer;
begin
  for i := 0 to m_EventHandler.Count - 1 do
    TNotifyEvent(m_EventHandler.Items[i])(self);
end;
So in der Art dachte ich mir das von der Anwendung her...

Im Moment denk ich, dass die einzige Lösung wirklich mit TMethod sein dürfte...
TMethod ist ein record mit 2 Eigenschaften Data und Code - richtig?

Naja morgen früh gehts weiter mit TMethod-Versuchen.
Markus Stein
 
Benutzerbild von Shivan
Shivan

Registriert seit: 5. Mär 2003
Ort: Ravensburg
131 Beiträge
 
Delphi 6 Enterprise
 
#9

Re: Ergänzung zu "Events in einer Liste"

  Alt 29. Mär 2007, 13:53
Jetzt habe ich endlich rausgefunden, warum sich meine Prozeduren nicht 1:1 zuweisen ließen...
Man muss den Umweg über eine Variable gehen...
bspw:
Delphi-Quellcode:
procedure myObj.test(Sender: TObject);
begin
  //....
end;

//...
var
  l_Event: TNotifyEvent;
begin
  l_Event := test;
  EventList.Add(TMethod(l_Event));
end;
Erst dann lässt sich so ein Event als TMethod casten.

Btw. die Unit GpLists enthält auch eine Klasse für das Verwalten von Methoden... jedoch finde ich die Variante als Ableitung von TList schicker, da dabei nur eine Liste verwendet wird...
Markus Stein
 
Thema geschlossen


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 09:37 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