![]() |
Ergänzung zu "Events in einer Liste"
Ich habe den
![]() 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:
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:
Sieht eigentlich wie eine normale typensichere Liste aus.
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; Die Implementierung:
Delphi-Quellcode:
Wie man sieht, wird jeweils nur die Adresse der Methode in der Liste gespeichert.
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; Einfügen von Events in die Liste:
Delphi-Quellcode:
EventList.Add(@MyEventHandler);
Aufrufen von Events aus der Liste:
Delphi-Quellcode:
Schwieriger wird es mit der Liste auf Methodenzeiger:
EventList.Items[i](ggf, die, Parameter);
Erst das Interface:
Delphi-Quellcode:
Und jetzt die Implementierung:
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;
Delphi-Quellcode:
Hier reicht es nich einfach nur den Pointer der Liste hinzuzufügen, da der Methodenzeiger eigentlich wie ein Record aufgebaut ist.
{ 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; Das hinzüfungen von Elementen geschieht folgendermaßen:
Delphi-Quellcode:
Und das aufrufen:
EventList.Add(Event);
Delphi-Quellcode:
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] |
Re: Ergänzung zu "Events in einer Liste"
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 :wink:) Gruß Der Unwissende |
Re: Ergänzung zu "Events in einer Liste"
Zitat:
Da hast du schon recht, deshalb habe ich bewust: Zitat:
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.
|
Re: Ergänzung zu "Events in einer Liste"
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] |
Re: Ergänzung zu "Events in einer Liste"
Hi,
wenn ich das richtig erkannt habe, geht die Eventlist hier nur mit einem definierten "Event-Typ". D.h. hier bspw.
Delphi-Quellcode:
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
TMyEvent = procedure of object;
Delphi-Quellcode:
definiert ist. Und dann entsprechend überall TMyEvent durch TMyNewEvent ersetzen?
TMyNewEvent = procedure (Sender: TObject) of object
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.
Delphi-Quellcode:
Jedoch wüsste ich nicht welchen Datentyp der Parameter haben sollte... Class of TMyEvent oder so ähnlich, was aber nicht geht.
.Create(p_MyDefinedEventHandlerType);...
Und im Add prüfen:
Delphi-Quellcode:
um zu vermeiden, dass falsche Methoden (mit falschen Parameter) in die Liste reinkommen.
procedure TEventList.Add(Item: TMyAllOfEventsParentType);
begin if not Item is m_MyDefinedEventHandlerType then raise Exception.Create('Falscher Funktionstyp verwendet'); Und dann an der Aufrufstelle bequem aufrufen via
Delphi-Quellcode:
(Eventlist.Items[i] as TMyblablaEvent)(param1, param2...);
|
Re: Ergänzung zu "Events in einer Liste"
Zitat:
Zitat:
Hoffe das hilft dir, Igel457 PS: Aber kann jetzt vielleicht endlich mal jemand diesen Beitrag mit dem in der CodeLib austauschen? Wäre nett... |
Re: Ergänzung zu "Events in einer Liste"
Zitat:
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 |
Re: Ergänzung zu "Events in einer Liste"
Zitat:
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:
Zitat:
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:
So in der Art dachte ich mir das von der Anwendung her...
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; 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. |
Re: Ergänzung zu "Events in einer Liste"
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:
Erst dann lässt sich so ein Event als TMethod casten.
procedure myObj.test(Sender: TObject);
begin //.... end; //... var l_Event: TNotifyEvent; begin l_Event := test; EventList.Add(TMethod(l_Event)); end; Btw. die Unit ![]() |
Alle Zeitangaben in WEZ +1. Es ist jetzt 23:40 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz