![]() |
Kopie eines Item aus der TObjectList
Hallo Zusammen,
die Frage vorab: Wenn ich aus einer TObjectList ein item hole etwa so:
Delphi-Quellcode:
bekomme ich dan den Pointer des items oder das item selbst?
CarPosColl := RawTagesauswertung.Items[i];
Ich hätte gerne eine kopie des items. Ich habe eine Klasse von TObject die Autos verwaltet. Dann eine Klasse TCarPosCollection von TObjectList die x Autos verwaltet. Und zuletzt eine Klasse Tagesauswertung ebenfals aus TObjectList welche x TCarPosCollection verwaltet. In einer Schleife lese ich aus der TObjectList "Tagesauswertung" ein paar items und übergebe diese zum verarbeiten in einer zweiten instanz von Klasse. So wird die übergabe der items gemacht:
Delphi-Quellcode:
Wenn ich die Schleife nur ein mal ausführe, funkt alles einwandfrei.
CarPosColl := RawTagesauswertung.Items[i];
Wenn ich die Schleife ein zweites mal durchführe und dabei vorher CarPosColl.free aufrufe, bekomme ich eine exception "EAccessViolation". Ich vermute das ich keine kopie übergebe sonder nur den Pointer des items welches in meiner Hauptliste steckt. Der Destructor versucht vermutlich das item aus der Haptliste zu bereinigen. Habs auch so probiert und bekomme beim free die exception "EPrivilege"
Delphi-Quellcode:
CarPosColl := TCarPosCollection.Create; CarPosColl := RawTagesauswertung.Items[i]; Hat jemand eine Idee? Hier meine Klassen aus das wesendliche reduziert: Gruß Kostas
Delphi-Quellcode:
type
TCarPos = class(TObject) private FZeitpunkt:TDateTime; FCarID:integer; Procedure SetZeitpunkt(value :TDateTime); function GetZeitpunkt:TDateTime; Procedure SetCarID(value :Integer); function GetCarID:Integer; public constructor Create; property Zeitpunkt: TDateTime read GetZeitpunkt write SetZeitpunkt; property CarID: Integer read GetCarID write SetCarID; end; TCarPosCollection =Class(TObjectList) private FZustand:Boolean; function Get(Index: Integer): TCarPos; procedure SetZustand(value :Boolean); function GetZustand:Boolean; public destructor Destroy; override; procedure Add(Item: TCarPos); property Items[Index: Integer]: TCarPos read Get; property Zustand:Boolean read GetZustand write SetZustand; end; TTagesauswertung =Class(TObjectList) private function Get(Index: Integer): TCarPosCollection; public destructor Destroy; override; procedure Add(Item: TCarPosCollection); property Items[Index: Integer]: TCarPosCollection read Get; end; destructor TCarPosCollection.Destroy; var i: Integer; begin for i := 0 to self.Count - 1 do self.Items[i].Free; inherited; end; procedure TCarPosCollection.Add(Item: TCarPos); begin inherited Add(Item); end; function TCarPosCollection.Get(index: integer):TCarPos; begin result := inherited Get(Index); end; destructor TTagesauswertung.Destroy; var i: Integer; begin for i := 0 to self.Count - 1 do self.Items[i].Free; inherited; end; procedure TTagesauswertung.Add(Item: TCarPosCollection); begin inherited Add(Item); end; function TTagesauswertung.Get(index: integer):TCarPosCollection; begin result := inherited Get(Index); end; |
Re: Kopie eines Item aus der TObjectList
Geb bei der 2. Liste einfach beim Constructor Aufruf ein False mit, dann gibt die TObjectList nicht die Items frei, die in ihr sind. Damit bleiben dann die in der Hauptliste auch weiterhin gültig.
Du bekommst einen Instanzenzeiger. Die TObjectListe verwaltet nur die Instanzenzeiger. |
Re: Kopie eines Item aus der TObjectList
Du überschreibst bei Deiner TCarPosCollection die Destroy-Methode der TObjectList und gibts alle Items frei. Das ist nicht nötig, das macht TObjectList schon.
Hier mal ein Grundgerüst für KLassen, die von TObjectList erben und somit ein Typecasting überflüssig machen: Ersetze Name durch Deine Klasse (TCarPos)
Delphi-Quellcode:
NameObjectList = class(TObjectList)
protected function GetItem(Index: Integer): Name; procedure SetItem(Index: Integer; AItem: Name); public function Add(AItem: Name): Integer; function Extract(Item: Name): Name; function Remove(AItem: Name): Integer; function IndexOf(AItem: Name): Integer; procedure Insert(Index: Integer; AItem: Name); function First: Name; function Last: Name; property Items[Index: Integer]: Name read GetItem write SetItem; default; end; implementation { NameObjectList } function NameObjectList.Add(AItem: Name): Integer; begin Result := inherited Add(AItem); end; function NameObjectList.Extract(Item: Name): Name; begin Result := Name(inherited Extract(Item)); end; function NameObjectList.First: Name; begin if Count = 0 then Result := nil else Result := Name(inherited First); end; function NameObjectList.GetItem(Index: Integer): Name; begin Result := Name(inherited Items[Index]); end; function NameObjectList.IndexOf(AItem: Name): Integer; begin Result := inherited IndexOf(AItem); end; procedure NameObjectList.Insert(Index: Integer; AItem: Name); begin inherited Insert(Index, AItem); end; function NameObjectList.Last: Name; begin if Count = 0 then Result := nil else Result := Name(inherited Last); end; procedure NameObjectList.SetItem(Index: Integer; AItem: Name); begin inherited Items[Index] := AItem; end; function NameObjectList.Remove(AItem: Name): Integer; begin Result := inherited Remove(AItem);end; |
Re: Kopie eines Item aus der TObjectList
Zitat:
|
Re: Kopie eines Item aus der TObjectList
Danke Muetze1 und sh17,
das ist interessant. Es wird also wirklich keine Kopie des items erzeugt. Ich benötige unbedingt eine Kopie der items, weil während dem Verarbeiten ich ein paar Eigenschaften der items verändere. Diese Änderung darf sich jedoch nicht im Master widerspiegeln. Ich habe leider noch nicht verstanden warum ich die ObjectList nicht leeren darf. Nochmals kurz beschrieben was ich eigentlich vor habe. Es gibt eine ObjectList Instanz "TTagesauswertung" die hält alle Daten vor die aus einem Select aus der DB befühlt wird. Die nenne ich jetzt mal Master. Jetzt möchte ich die Daten verarbeiten. Ich erzeuge eine weitere, leere Instanz von "TTagesauswertung". Die nenne ich mal Slave. Danach gehe ich alle items von Master durch und hole mir ein paar items zum verarbeiten in die Slave. Dieser Vorgang wird mehrmals wiederholt. Dabei sollen die Master unverändert bleiben. Wenn ich also fertig bin, rufe ich Slave.free und Slave := TTagesauswertung.Create; auf. Ihr meint jetzt, der constructor darf die items nicht freigeben. Sicherlich habt Ihr Recht. Ich verstehe es nur noch nicht warum. Die items von Master dürfen sicherlich nicht freigegeben werden, aber die items von Slave müssen doch freigegeben werden oder? Wie bereits erwähnt, wird Slave öfters erzeugt, befüllt und wieder zerstört. Habt Ihr eine Idee? |
Re: Kopie eines Item aus der TObjectList
TObjectList bietet einen Mechanismus zum Verwalten seiner Items. Ruft man TObjectList.Create(true) auf, gehören die Items dieser Objectlist und sie werden bei ObjectList.clear bzw ObjectList.Free von dieser wieder freigegeben.
Ruft man TObjectList.Create(false) auf, muss man sich selbst um die Freigaben der Items kümmern. Um eine Kopie eines Items zu bekommen, musst Du dieses Object TCarPos noch z.b. um die funktionen AssignTo und Clone erweitern:
Delphi-Quellcode:
Dann machst Du einfachTCarPos public procedure AssignTo(_Dest : TCarPos); function Clone : TCarPos; end; procedure TCarPos.AssignTo(_Dest : TCarPos); begin _Dest.Zeitpunkt := Zeitpunkt; ... end; function TCarPos.Clone : TCarPos; begin Result := TCarPos.Create; AssignTo(Result); end;
Delphi-Quellcode:
schon hast Du eine Kopie, die Du mit slave.clear wieder löschst.for i := 0 to master.count-1 do slave.add(master[i].clone) idealer Weise erweiterst Du Deine Objektlist auch um AssignTo und Clone um sich die Schleife zu sparen |
Re: Kopie eines Item aus der TObjectList
ich mache mich sofort an die Arbeit.
Tausend Dank und schöne Grüße. Kostas |
Re: Kopie eines Item aus der TObjectList
Hallo sH17,
es funkt beste Sahne mit dem Clonen. Hab auch die constructoren aus beiden TObjectList entfernt. Ein bisschen unwohl ist mir noch bei dieser Sache, weil ich noch nicht sattelfest bin in OOP. (Ich habe es bis jetzt umgehen können. ;-)) Wenn ich das free der TObjectList aufrufe werden dann auch alle geklonte items gelöscht? Ist es eigentlich Möglich memory leaks zu finden? Hier noch meine drei Klassen. Wenn es Dir möglich währe nochmals drüber zu schauen. Besten Dank nochmals sH17 und schöne Grüße.
Delphi-Quellcode:
TCarPos = class(TObject)
private FZeitpunkt:TDateTime; FCarID:integer; Procedure SetZeitpunkt(value :TDateTime); function GetZeitpunkt:TDateTime; Procedure SetCarID(value :Integer); function GetCarID:Integer; public constructor Create; property Zeitpunkt: TDateTime read GetZeitpunkt write SetZeitpunkt; property CarID: Integer read GetCarID write SetCarID; procedure AssignTo(var Dest : TCarPos); function Clone: TCarPos; end; TCarPosCollection =Class(TObjectList) private FZustand:Boolean; function Get(Index: Integer): TCarPos; public procedure Add(Item: TCarPos); property Items[Index: Integer]: TCarPos read Get; procedure AssignTo(var Dest : TCarPosCollection); function Clone: TCarPosCollection; end; TTagesauswertung =Class(TObjectList) private function Get(Index: Integer): TCarPosCollection; public procedure Add(Item: TCarPosCollection); property Items[Index: Integer]: TCarPosCollection read Get; end; { TCarPos } constructor TCarPos.Create; begin FZeitpunkt:=0.0; FCarID:=0; end; procedure TCarPos.AssignTo(var Dest: TCarPos); begin Dest.FZeitpunkt := FZeitpunkt; Dest.FCarID := FCarID; end; function TCarPos.Clone: TCarPos; begin result := TCarPos.Create; AssignTo(result); end; { TCarPosCollection } procedure TCarPosCollection.Add(Item: TCarPos); begin inherited Add(Item); end; function TCarPosCollection.Get(index: integer):TCarPos; begin result := inherited Get(Index); end; procedure TCarPosCollection.AssignTo(var Dest: TCarPosCollection); var i:integer; begin Dest.FZustand := FZustand; {kopiere alle items vom typ TCarPos} for i := 0 to count-1 do Dest.add(items[i].clone); end; function TCarPosCollection.Clone: TCarPosCollection; begin result := TCarPosCollection.Create; AssignTo(result); end; { TTagesauswertung } procedure TTagesauswertung.Add(Item: TCarPosCollection); begin inherited Add(Item); end; function TTagesauswertung.Get(index: integer):TCarPosCollection; begin result := inherited Get(Index); end; |
Re: Kopie eines Item aus der TObjectList
Ich sehe soweit keine Probleme. Sollte alles so klappen. Die Add() Funktion ist nicht unbedingt nötig - ansonsten aber auch nicht falsch.
|
Re: Kopie eines Item aus der TObjectList
Solange TObjectList.Create(true) aufgerufen wurde, werden bei Free auch die Items freigegeben.
Noch eine Anmerkung zu
Delphi-Quellcode:
In diesem Fall ist die Verwendung von Dest.FZeitpunkt und Dest.FCarID kein Problem, da die Set-Methoden der Properties Zeitpunkt und CarID auch nix anderes machen. Sollte allerdings in den Set-Methoden weiterer Code stehen, der mit dem Wert vorher noch etwas macht, dann musst Du folgendes aufrufen:
procedure TCarPos.AssignTo(var Dest: TCarPos);
begin Dest.FZeitpunkt := FZeitpunkt; Dest.FCarID := FCarID; end;
Delphi-Quellcode:
Bei
procedure TCarPos.AssignTo(var Dest: TCarPos);
begin Dest.Zeitpunkt := Zeitpunkt; Dest.CarID := CarID; end;
Delphi-Quellcode:
wäre ggf noch ein Dest.Clear zu Beginn sinnvoll.
procedure TCarPosCollection.AssignTo(var Dest: TCarPosCollection);
var i:integer; begin Dest.FZustand := FZustand; {kopiere alle items vom typ TCarPos} for i := 0 to count-1 do Dest.add(items[i].clone); end; |
Re: Kopie eines Item aus der TObjectList
Zitat:
habe es so belassen um beim add nicht typecasten zu müssen. Entfernt, ist es dann wie generics bei c# Gruß Kostas |
Re: Kopie eines Item aus der TObjectList
Zitat:
Um zu erzwingen, dass die geerbte Methode gerufen wird musst Du direkt die Methode rufen, also:
Delphi-Quellcode:
das gleiche gilt für die getter-Methode.
procedure NameObjectList.SetItem(Index: Integer; AItem: Name);
begin inherited SetItem(Index,AItem); end; p.s.: Das ist einer der wenigen Fälle, wo man nicht die Property, sondern die dazugehörigen Getter/Setter aufruft, ansonsten hast Du Recht mit der bevorzugten Verwendung der Property. |
Re: Kopie eines Item aus der TObjectList
Zitat:
@Sidorion: Probier bitte deine Behauptung mal aus. Die GetItem() und SetItem() sind privat in der TObjectList und nicht virtual. Damit werden die SetItem() und GetItem() von TObjectList versteckt und sind nicht mehr aufrufbar. Somit kannst du schlecht ein Inherited SetItem() aufrufen. Der hier gepostete Weg ist der richtige. Mit dem Inherited Items[] greift er auf die Items der TObjectList zu und diese nutzen ihre (in der Ableitung versteckten) GetItem() und SetItem() Methoden. Die TObjectList könnte die GetItem() und SetItem() von der Ableitung nur aufrufen, wenn diese überschrieben wurden - und das sind sie nicht bzw. können sie nicht. Probier dein geschriebenes bitter selber aus! /EDIT: Nachgeschaut: SetItem() und GetItem() sind protected und nicht virtuell. Damit können Sie zwar aufgerufen werden mit dem Inherited, aber die o.g. Schleife kann nicht eintreten. Der Inherited Items[] Zugriff greift auf die GetItem() und SetItem() der TObjectList zu. Diese beiden Methoden werden nur versteckt, nicht aber überschrieben und somit gibt es keine Schleife. Eine Schleife würde es beim Zugriff auf Items[] ohne Inherited geben. |
Re: Kopie eines Item aus der TObjectList
Zitat:
Zitat:
Ich werde sofort auf Properties umstellen. Zitat:
Es funkt alles einwandfrei. Super Leute besten Dank und schöne Grüße. Kostas |
Re: Kopie eines Item aus der TObjectList
@Muetze1: Hmmm. nicht virtuell... dann vergiss meine Behauptung mit der Schleife (in diesem Fall). Jedoch ist die Aussage an sich nicht falsch, da sobald ein Setter/Getter virtuell ist kommt es eben zu diesem Problem (hab ich schon erlebt). Darum stimmt die Aussage 'Inherited SetItem' zu rufen immernoch, weil niemand kann garantieren, dass TObjectList.SetItem für immer statisch ist :wink: Den 'groben Schnitzer' nehme ich also offiziell zurück :mrgreen:
|
Re: Kopie eines Item aus der TObjectList
Nö, weil wenn GetItem() SetItem virtuell sind, dann würdest du diese immernoch nicht überschreiben. Statt dessen würdest du eine Warnung vom Compiler bekommen (verdeckt virtuelle Methode aus der Basisklasse TObjectList, etc) aber trotzdem würdest du nichts überschreiben. Die alten werden verdeckt und du implementierst ganz neue. Dadurch würde inherited immerncoh die Originalen Methoden von TObjectList aufrufen und nicht die eigenen.
Erst wenn du in deiner Ableitung der Warnung die Grundlage entziehst und die virtuellen Methoden richtig überschreibst mit Override, dann würde dein geschildertes Szenario eintreten. So lange du aber die Warnung stehen lässt oder aber mit ReIntroduce diese unterdrückst, hast du keine Probleme mit dem Inherited SetItem() / GetItem(). |
Alle Zeitangaben in WEZ +1. Es ist jetzt 05:41 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