![]() |
Remove in Generic TObjectList
Hallo alle zusammen,
nachdem ich mir Delphi2009 gekauft und installiert habe, wollte ich die generischen Listen der neuen Version nutzen. Ich habe also die Objectlisten (TObjectList) durch generische (z.B. TObjectList<TRecord>) ersetzt. Damit wollte ich mir Typkonvertierungen beim Zugriff auf Elemte der Liste ersparen. Der Compiler hat das ganze auch ohne Fehlermeldung compiliert. Als ich aber in meinem Programm den Menüpunkt zum Löschen eines einzelnen Elementes der Liste aufgerufen habe, kam eine AV. Das Löschen sollte über Remove erfolgen. Das prinzipielle Problem habe ich in folgendem Testprogramm dargestellt. Bei Aufruf von Remove innerhalb Button2Click kommt die AV. Löschen über Delete funktioniert dagegen.
Delphi-Quellcode:
Kann mir jemand sagen, wo der Fehler liegt. Habe ich etwa das ganze Thema mit den Generics falsch verstanden?
unit Unit3;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Generics.Collections, Dialogs, StdCtrls; type TForm3 = class(TForm) Button1: TButton; Button2: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); private { Private-Deklarationen } public { Public-Deklarationen } end; TRecord = class(TObject) Name:String; X,Y:Double; end; var Form3: TForm3; RecLst : TObjectList<TRecord>; implementation {$R *.dfm} procedure TForm3.Button1Click(Sender: TObject); var I: Integer; Rec:TRecord; begin for I := 0 to 4 do begin Rec:=TRecord.Create; Rec.X:=Random; Rec.Y:=Random; RecLst.Add(Rec); end; end; procedure TForm3.Button2Click(Sender: TObject); var Rec:TRecord; begin Rec:=RecLst[0]; RecLst.Remove(Rec); end; initialization RecLst :=TObjectList<TRecord>.Create; finalization RecLst.Free; end. Bin für jeden Hinweis dankbar! |
Re: Remove in Generic TObjectList
Hallo,
wenn ich das richtig verstanden habe, benötigt die Objektliste einen comparer zum Auffinden von Elementen - auch bei IndexOf(). Da ich aber selbst gerade dabei bin, meine ersten Gehversuche mit Delphi 2009 zu machen, wäre es gut, wenn ein "Wissender" den folgenden Code einmal prüfen könnte.
Delphi-Quellcode:
Gruß Hawkeye
// uses Generics.Defaults
function CompareNames (const Left, Right: TRecord): Integer; begin Result := CompareText(Left.Name, Right.Name); end; initialization RecLst := TObjectList<TRecord>.Create ( TComparer<TRecord>.Construct(CompareNames) ); finalization RecLst.Free; end. |
Re: Remove in Generic TObjectList
Zitat:
|
Re: Remove in Generic TObjectList
[OT]
Zitat:
[/OT] |
Re: Remove in Generic TObjectList
Zitat:
|
Re: Remove in Generic TObjectList
Ich kann mir nicht vorstellen, dass TObjectList sich so einfach Records unterschieben lassen.
Man muss Objekte in TObjectList speichern, weil in der Methode Remove intern <dasobjectausderliste>.Free; aufgerufen wird. Verweisst der Zeiger nicht auf ein richtiges Objekt, knallt es bei .Free In deinem Fall wäre TList die richtige Basisklasse. |
Re: Remove in Generic TObjectList
Heisst das etwa, dass Delphi generische Parameter ungeprüft annimmt und einfach aufs geratewohl irgendwelche Pointer aufruft?
|
Re: Remove in Generic TObjectList
|
Re: Remove in Generic TObjectList
Hallo zusammen,
danke für Eure Hinweise und Diskussion. Das Update #1 habe ich gestern eingespielt. Am Verhalten in dem beschriebenen Fall hat sich aber nichts geändert. Der Objectliste werden keine Records übergeben. Obwohl die Struktur irreführenderweise Record heisst, ist es eine Klasse (siehe Deklaration). Wenn ich das ganze mit einer TObjectList ohne Generics mache klappt es ohne Probleme. Ich muss dann aber bei jedem Zugriff auf ein Objekt der Liste eine Typumwandlung machen. Also z.B. Rec:=TRecord(RecLst[0]); Das wollte ich mir eigentlich sparen ! |
Re: Remove in Generic TObjectList
Nach längerer Zeit hat mich dieser Punkt wieder einmal geärgert. Ich habe das Thema in QualityCentral eingestellt (#75853) und einen Verweis auf ein bereits diskutiertes Thema bekommen (#67272) - dort war auch der Grund für die Fehlermeldung und eine Abhilfe zu finden. Falls es jemand interessiert, dann versuche ich es mal mit meinen Worten zu beschreiben:
Der Fehler liegt in der Unit Generics.Collections im Constructor von TObjectList<T>:
Delphi-Quellcode:
TObjectList<T> ist von TList<T> abgeleitet. In der Klasse TList<T> gibt es aber keinen Constructor mit einem Parameter AOwnsObject. Damit läuft der Aufruf inherited ins Leere (und erzeugt auch keinen Compiler-Fehler !). Das heisst TObjectList wird nicht initialsiert und es wird kein auch keine Comparer erzeugt (dies geschieht eigentlich in TList.Create). Damit erzeugen alle nachfolgenden Aufrufe wie etwa
constructor TObjectList<T>.Create(AOwnsObjects: Boolean);
begin inherited; FOwnsObjects := AOwnsObjects; end;
Delphi-Quellcode:
eine AV.
ObjectList.Extract(Obj)
ObjectList.Remove(Obj) ObjectList.IndexOf(Obj) Lösungen: - Korrektur in Generics.Collections.pas:
Delphi-Quellcode:
oder
constructor TObjectList<T>.Create(AOwnsObjects: Boolean);
begin inherited Create(); FOwnsObjects := AOwnsObjects; end; - Meiden des Aufrufs
Delphi-Quellcode:
stattdessen:
ObjectList := TObjectList<TNewObject>.Create;
Delphi-Quellcode:
Wie schon gesagt, die Lösung kommt nicht von mir, sondern wurde von Anfrey Tsiruljov und Wang Rui (die ich hier zumindest erwähnen möchte) in QualityCentral gestellt.
ObjectList := TObjectList<TNewObject>.Create(TComparer<TNewObject>.Default);
Mich wundert nur, dass der Fehler nicht in einem Delphi Update behoben wurde, da diese Fehler-Reports schon einige Monate alt sind. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 10:37 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 by Thomas Breitkreuz