Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Fragen zu Generic TList (https://www.delphipraxis.net/191538-fragen-zu-generic-tlist.html)

norwegen60 25. Jan 2017 21:27

Delphi-Version: 10 Seattle

Fragen zu Generic TList
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo,

ich habe mich etwas mit Generic TList beschäftigt und hänge nun bei ein paar Punkten. Hier zunächst Code-Auszüge (im Anhang das ganze Projekt).
Delphi-Quellcode:
  TMyClass = class
  private
    FCounter, FValue: Integer;
  public
    property Counter: Integer read FCounter write FCounter;
    property Value:  Integer read FValue write FValue;
  end;

  TMyList = class(TList<TMyClass>)
  private
    FFreeOnDelete: Boolean;
    procedure Notify(Ptr: Pointer; Action: TListNotification); virtual;
  public
    constructor Create(bValue: Boolean = true);
  end;

var
  MyList:    TMyList;
  MyCopyList: TMyList;


  MyList := TMyList.Create;
  MyCopyList := TMyList.Create(false); // In Copyliste bei Delete Daten nicht löschen

  // Datenliste erzeugen
  for i := 0 to 50 do
  begin
    Values := TMyClass.Create;
    Values.Counter := i;
    Values.Value := random(200);
    MyList.Add(Values);
  end;

  // Datenliste in Memofeld anzeigen
  for i := 0 to MyList.Count - 1 do
  begin
    Values := MyList.Items[i];
    Memo1.Lines.Add(format('%d: %d, %d', [i, Values.Counter, Values.Value]));
  end;

  // Datenliste kopieren
  for i := 0 to MyList.Count - 1 do
  begin
    Values := MyList.Items[i];
    MyCopyList.Add(Values);
  end;

  // Jeden 10 Datensatz in kopierter Liste löschen
  MyCopyList.Delete(40);
  MyCopyList.Delete(30);
  MyCopyList.Delete(20);
  MyCopyList.Delete(10);           // gelöscht werden aber die letzten 4 Datensätze

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  MyCopyList.Free;
  MyList.Free;
end;

{ TMyList }

constructor TMyList.Create(bValue: Boolean = true);
begin
  inherited Create;
  FFreeOnDelete := bValue;
end;

procedure TMyList.Notify(Ptr: Pointer; Action: TListNotification);
begin
  if (FFreeOnDelete) and (Action = lnDeleted) then
    TObject(Ptr).Free;
  // inherited Notify(Ptr, Action);
end;
Folgende Fragen:
  1. Gibt es grundsätzliche Fehler bei der Behandlung
  2. In der Originalliste funktioniert
    Delphi-Quellcode:
    MyList.Delete(30);
    einwandfrei, in der kopierten Liste wird mit jedem Aufruf der letzte Satz gelöscht. Warum?
  3. Notify zum löschen des Speicherplatz funktioniert nicht und inherited führt gleich zu Fehler. Irgendwie scheint der Header in XE10 nicht dem in der Hilfe zu entsprechen. Aber wie geht es richtig?

Danke für eure Unterstützung
Gerd

SProske 25. Jan 2017 21:44

AW: Fragen zu Generic TList
 
Hat es einen bestimmten Grund, dass du nicht TObjectList<TMyClass> verwendest?

ensaron 26. Jan 2017 06:37

AW: Fragen zu Generic TList
 
Deine Funktion wird korrekt ausgeführt, aber deine Ausgabefunktion hat einen Fehler:

Delphi-Quellcode:
Memo2.Clear;
for i := 0 to MyCopyList.Count - 1 do
begin
  Values := MyList.Items[i];
  Memo2.Lines.Add(format('%d: %d, %d', [i, Values.Counter, Values.Value]));
end;
Du zeigst die Einträge der originalen Liste an, benutzt aber die Anzahl der kopierten Liste als Maximum.

haentschman 26. Jan 2017 07:56

AW: Fragen zu Generic TList
 
Moin...:P
Du macht es dir schwerer als es in Wirklichkeit ist. :zwinker: Bei deinen 2 Listen gibt es einiges zu beachten.
1. Eine Masterlist die die Werte hällt und die Werte freigibt. (TObjectList)
2. Die Liste mit den kopierten Werten hällt. (TList) Diese Liste hällt nur die Pointer der Values aus der Masterliste.
3. Die Einträge können direkt mit TList.Delete aus der Liste entfernt werden. Das Original bleibt davon unberührt.

Delphi-Quellcode:
for i := 0 to 50 do
  begin
    Values := TMyClass.Create; // ein Pointer wird erzeugt
    Values.Counter := i;
    Values.Value := random(200);
    MyList.Add(Values); // ein Pointer liegt in der Liste und wird mit TList [B]nicht[/B] freigegeben
  end;
...wie ich auch in dem anderen Thread sagte, du solltest die TObjectList benutzen.
Delphi-Quellcode:
for i := 0 to MyList.Count - 1 do
  begin
    Values := MyList.Items[i];
    MyCopyList.Add(Values);
  end;
...das Legen in die MyCopyList legt nur den Pointer der Instanz aus der MasterList in die MyCopyList.
Delphi-Quellcode:
TMyList = class(TList<TMyClass>)
  private
    FFreeOnDelete: Boolean; // nicht notwendig da die Pointer in dieser Liste nicht freigegeben werden müssen
    procedure Notify(Ptr: Pointer; Action: TListNotification); virtual; // nicht notwendig da die Pointer in dieser Liste nicht freigegeben werden müssen
  public
    constructor Create(bValue: Boolean = true);
  end;
... die Freigabe der Values braucht hier nicht erfolgen. Das erledigt die MasterList über OwnsObjects.

:wink:

DeddyH 26. Jan 2017 08:56

AW: Fragen zu Generic TList
 
Die Kopie-Liste kann doch auch eine TObjectList sein, nur eben mit OwnsObjects auf false, oder mache ich gerade einen Denkfehler?

haentschman 26. Jan 2017 09:00

AW: Fragen zu Generic TList
 
Zitat:

Die Kopie-Liste kann doch auch eine TObjectList sein, nur eben mit OwnsObjects auf false, oder mache ich gerade einen Denkfehler?
...nö. :zwinker: Es ging auch darum die Unterschiede von TObjectList und TList gegenüberzustellen...

norwegen60 26. Jan 2017 09:19

AW: Fragen zu Generic TList
 
Das mit den TObjectList hatte ich schon verstanden und will es mir auch noch anschauen. Zuerst wollte ich aber mal die TList verstehen da eine für mich wichtige Anwendung mit TList<> arbeitet und ich noch nicht abschätzen kann welche Auswirkungen ein Wechsel auf TObjectList hat. :(

Zweifel an meinem Verständnis hatte ich aufgrund der vermeintlich falsch gelöschten Daten in der Kopierliste. Deshalb Dank an Ensaron's Hinweis, dass es sich um einen typischen Paste&Copy Fehler handelt.:-D

Jetzt würde ichnur noch gerne verstehen, wie ich das Notify korrekt implementiere.

Danach stürze ich mich auf TObjectList. Versprochen.:thumb:

Der schöne Günther 26. Jan 2017 09:24

AW: Fragen zu Generic TList
 
Zitat:

Zitat von norwegen60 (Beitrag 1360007)
Jetzt würde ichnur noch gerne verstehen, wie ich das Notify korrekt implementiere.

Schau mal in die WARNUNG-Meldungen des Compilers. Da steht's eigentlich :o

Fritzew 26. Jan 2017 10:04

AW: Fragen zu Generic TList
 
Zitat:

Schau mal in die WARNUNG-Meldungen des Compilers. Da steht's eigentlich
[dcc32 Warnung] fo_Tlist.pas(56): W1010 Methode 'Notify' verbirgt virtuelle Methode vom Basistyp 'System.Generics.Collections.TList<fo_Tlist.TMyCla ss>'

Du erzeugst eine neue Notify Procedure

mach es so:

Delphi-Quellcode:
 TMyList = class(TList<TMyClass>)
  private
    FFreeOnDelete: Boolean;
// Die Notify ist protected
   protected
    procedure Notify(const Ptr: tMyclass; Action: TCollectionNotification); override; // Überschreiben nicht virtual
  public
    constructor Create(bValue: Boolean = true);
  end;

norwegen60 26. Jan 2017 10:16

AW: Fragen zu Generic TList
 
Die Warnmedlung hatte ich gesehen, nur wusste ich nichts damit anzufangen. Auch nachdem ich noch mal in Netz gesucht habe. Gut dass du mich direkt darauf gelüpft hast.

Viel weiter gekommen bin ich aber noch nicht, denn jetzt bin ich wieder bei
Zitat:

[dcc32 Fehler] fo_Tlist.pas(67): E2037 Deklaration von 'Notify' unterscheidet sich von vorheriger Deklaration
und wenn ich es auf
Delphi-Quellcode:
procedure Notify(const Item: T; Action: TCollectionNotification); override;
ändere mag er den Typ T nicht.

Fritzew 26. Jan 2017 10:19

AW: Fragen zu Generic TList
 
Ich hatte die Signatur noch mal geändert, (war nur so aus dem Kopf),
mit der aktuellen Signatur sollte es gehen siehe oben

norwegen60 26. Jan 2017 11:32

AW: Fragen zu Generic TList
 
Noch mal 1mm weiter aber immer noch nicht am Ziel.

Jetzt streikt der Aufruf
Delphi-Quellcode:
procedure TMyList.Notify(const Ptr: TMyclass; Action: TCollectionNotification); // Überschreiben nicht virtual
begin
  if (FFreeOnDelete) and (Ptr<>nil) and (Action = cnRemoved) then
    Ptr.Free; // Ungültige Zeigeroperation
  inherited Notify(Ptr, Action);
end;
Beim Aufruf von Ptr.Free kommt es zu einer Exception "Ungültige Zeigeroperation". Dispose(Ptr) ging auch nicht.

Ausserdem:
Ich bin dr Meinung, dass inherited nach meiner Aktion erfolgen soll aber auf der Suche fan ich auch Beispiele, wo zuerst inherited aufgerufen wurde. Spielt es eine Rolle?

Klaus01 26. Jan 2017 11:38

AW: Fragen zu Generic TList
 
.. nur mal ein Schuss ins Blaue - an dem const Parameter kann es nicht liegen?

oder Ptr ist bereits woanders freigegeben und nicht auf nil gesetzt worden .

Grüße
Klaus

haentschman 26. Jan 2017 11:54

AW: Fragen zu Generic TList
 
Hallöle...8-)
Delphi-Quellcode:
procedure TMyList.Notify(const Ptr: TMyclass; Action: TCollectionNotification); // Überschreiben nicht virtual
begin
  if (FFreeOnDelete) and (Ptr<>nil) and (Action = cnRemoved) then
    Ptr.Free; // Ungültige Zeigeroperation
  inherited Notify(Ptr, Action);
end;
..du gibst immer noch den Pointer frei der in der Gesamtliste beibehalten werden soll. Die Liste muß dich über nix in informieren... (Notify kann weg) :roll: Einfach den Listen Eintrag mit DELETE entfernen...fertsch. 8-) Die Instanzen der Hauptliste werden mit der TObjectList, wenn du endlich mal eine hast, weggeräumt. 8-)

Zitat:

und ich noch nicht abschätzen kann welche Auswirkungen ein Wechsel auf TObjectList hat.
Es gibt keine Auswirkungen. Der Unterschied von TObjectList und TList besteht darin das die TObjectList ihre enthaltenen Instanzen wegräumt.

norwegen60 26. Jan 2017 14:33

AW: Fragen zu Generic TList
 
Zitat:

.. nur mal ein Schuss ins Blaue - an dem const Parameter kann es nicht liegen?
Den Schuss hatte ich auch schon probiert. Wenn ich CONST entferne meckert er wieder, dass sich die Definition von der vorherigen Unterscheidet. Und dass der Ptr <> nil ist habe ich ja abgesichert.

Zitat:

Die Instanzen der Hauptliste werden mit der TObjectList, wenn du endlich mal eine hast, weggeräumt.
Eigentlich wollte ich doch nur rausbekommen wie das mit dem Notify funktioniert nachdem in der Hilfe doch steht
Zitat:

Hinweis: Der für das Element reservierte Speicherplatz wird von Delete nicht freigegeben. Das Objekt kann freigegeben werden, indem die Methode Notify überschrieben wird.
Und warum raten sie in http://www.delphipraxis.net/190843-t...terschied.html zu TList beim arbeiten mit Klassen? Wenn OwnObjects wirklich der einzige Unterschied ist, sollte es doch egal sein.

haentschman 26. Jan 2017 17:24

AW: Fragen zu Generic TList
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallöle...:P
Zitat:

Und warum raten sie in http://www.delphipraxis.net/190843-t...terschied.html zu TList beim arbeiten mit Klassen? Wenn OwnObjects wirklich der einzige Unterschied ist, sollte es doch egal sein.
Das OwnObjects ist der einzige Unterschied...aber der macht das. :thumb: Im Anhang ein Projekt wo die Speicherlecks angezeigt werden...einmal TList und einmal TObjectList (siehe Quelltext anzeigen)
Zitat:

TObjectList ist nichts weiter als eine Unterklasse von TList. Das einzige was sie zusätzlich macht ist das Freigeben der enthaltenen Objekte wenn OwnsObjects == True ist.
...dazu gibt es nichts mehr zu sagen. :wink:
Zitat:

Eigentlich wollte ich doch nur rausbekommen wie das mit dem Notify funktioniert nachdem in der Hilfe doch steht
Das Notify ist dafür da, das die "Logik" über Änderungen an der Liste informiert wird um weitere Aktionen auszulösen. Dazu gehört nicht die Freigabe der Listeneinträge. Da macht die Liste selbst ohne eine zusätzliche Zeile (TObjectList) :zwinker:

Hinweis:
Zitat:

raten sie
...auch wenn wir Ü(irgendwas) sind sind wir beim du...Respekt vorausgesetzt. :zwinker:

norwegen60 26. Jan 2017 19:15

AW: Fragen zu Generic TList
 
Hallo,

Zitat:

...auch wenn wir Ü(irgendwas) sind sind wir beim du...Respekt vorausgesetzt.
warst nicht du gemeint sondern die anderen in dem erwähnten Thread.:-D

Weil ich es eben so verstanden hatte, dass TObjectList nur das zusätzlich Löschen hat, habe ich nicht verstanden warum dann in dem andern Thread zu TList geraten wird.

In meinem Projekt habe ich schon auf TObjektList umgestellt. Dein Leckanzeiger werde ich gleich mal ausprobieren:thumb:


Alle Zeitangaben in WEZ +1. Es ist jetzt 05:26 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