Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Remove in Generic TObjectList (https://www.delphipraxis.net/123494-remove-generic-tobjectlist.html)

SteffenSchm 3. Nov 2008 10:42


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:
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.
Kann mir jemand sagen, wo der Fehler liegt. Habe ich etwa das ganze Thema mit den Generics falsch verstanden?

Bin für jeden Hinweis dankbar!

Hawkeye219 3. Nov 2008 20:14

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:
// 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.
Gruß Hawkeye

Uwe Raabe 3. Nov 2008 20:46

Re: Remove in Generic TObjectList
 
Zitat:

Zitat von Hawkeye219
wenn ich das richtig verstanden habe, benötigt die Objektliste einen comparer zum Auffinden von Elementen - auch bei IndexOf().

Das ist genau richtig! Und der Code tut's auch...

Khabarakh 3. Nov 2008 21:19

Re: Remove in Generic TObjectList
 
[OT]
Zitat:

Zitat von Hawkeye219
wenn ich das richtig verstanden habe, benötigt die Objektliste einen comparer zum Auffinden von Elementen - auch bei IndexOf().

Logisch ist das schon, aber es kann doch nicht sein, dass das mit einer AV beantwortet wird :shock: ?
[/OT]

Dax 3. Nov 2008 21:21

Re: Remove in Generic TObjectList
 
Zitat:

Zitat von Khabarakh
[OT]
Zitat:

Zitat von Hawkeye219
wenn ich das richtig verstanden habe, benötigt die Objektliste einen comparer zum Auffinden von Elementen - auch bei IndexOf().

Logisch ist das schon, aber es kann doch nicht sein, dass das mit einer AV beantwortet wird :shock: ?
[/OT]

Es ist eine effektive Demonstration der Ausgereiftheit von Generics in Delphi. ;-)

sx2008 3. Nov 2008 23:10

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.

Dax 3. Nov 2008 23:13

Re: Remove in Generic TObjectList
 
Heisst das etwa, dass Delphi generische Parameter ungeprüft annimmt und einfach aufs geratewohl irgendwelche Pointer aufruft?

Bernhard Geyer 4. Nov 2008 06:10

Re: Remove in Generic TObjectList
 
Schon Update #1 eingespielt? In der Fix-Liste sind ein paar Einträge zu Generics vorhanden.

SteffenSchm 4. Nov 2008 07:26

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 !

SteffenSchm 23. Jul 2009 13:47

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:
constructor TObjectList<T>.Create(AOwnsObjects: Boolean);
begin
  inherited;
  FOwnsObjects := AOwnsObjects;
end;
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
Delphi-Quellcode:
ObjectList.Extract(Obj)
ObjectList.Remove(Obj)
ObjectList.IndexOf(Obj)
eine AV.

Lösungen:

- Korrektur in Generics.Collections.pas:
Delphi-Quellcode:
constructor TObjectList<T>.Create(AOwnsObjects: Boolean);
begin
  inherited Create();
  FOwnsObjects := AOwnsObjects;
end;
oder
- Meiden des Aufrufs
Delphi-Quellcode:
  ObjectList := TObjectList<TNewObject>.Create;
stattdessen:
Delphi-Quellcode:
  ObjectList := TObjectList<TNewObject>.Create(TComparer<TNewObject>.Default);
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.
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 19:13 Uhr.
Seite 1 von 2  1 2      

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