![]() |
Von TStringList abgeleitete Klasse inkl. Objecten speichern
Hallo,
Grundproblem: Ich möchte in meinem Programm viele Daten verwalten. Diese Daten speichere ich nicht in Records, sondern in TmeineDaten = Class(TObject) End; TmeineDaten enthält dann Strings, Integer und alles mögliche andere. Diese Objecte wiederum verwalte ich alle in einer TStringList; TStringList deshalb, weil die Objecte alle mit Ihrem Namen abgelegt werden und ich dann über Index:=IndexOf('irgendwas') bzw. z.B. (Objects[Index] As TmeineDaten).TollerString:='Hallo'; gut drankomme. Mir ist SaveToFile() bekannt, was aber die Inhalte meiner Objects nicht mit speichert. TFileStream.WriteComponent() klappt leider auch nicht, weil ich meine Klasse nicht von TComponent abgeleitet habe... Ich möchte gern eine Methode SaveToFile() in meiner Haupt-Klasse (eben abgeleitet von TStringList) implemetieren, die die Object-Daten mit speichert und ohne einen riesigen Aufwand zu machen. [edit]Ziel ist es später beim Programmstart, die ganzen Daten wieder in die Liste laden zu können[/edit] Gibt es hierfür eine Lösung/Ideen? Ich hoffe, mein Problem verständlich rübergebracht zu haben. Falls trotzdem Fragen sind, bitte stellen! Danke, Alex |
AW: Von TStringList abgeleitete Klasse inkl. Objecten speichern
Du könntest eine Klasse von TStringList ableiten mit einem eigenen SaveToFile. Dort machst du für jedes Attribut, das du hast, mit z.b. einem MemoryStream WriteBuffer.
Delphi-Quellcode:
Zum auslesen machst du einfach das gleich wie beim Speichern nur eben ReadBuffer
type
TMeinDaten = class property ABC : integer read fAbc write fAbc; end; //deine savetofile funktion ms:=TMemoryStream.create; ms.WriteBuffer(meinedaten.abc,sizeof(meinedaten.abc)); //usw, falls mehrere attrbute ms.savetofile('MeinDateiname.abc'); ms.free; |
AW: Von TStringList abgeleitete Klasse inkl. Objecten speichern
Danke für die Antwort!
Die Idee ist gut, aber in meinem Fall nicht praktikabel. Habe ich doch tatsächlich vergessen zu erwähnen, dass ich das ganze später natürlich wieder laden möchte (geändert, siehe oben). Das Problem ist, dass ich dann jedes Object wieder initialiseren muss etc. pp. Das erzeugt damit einen riesigen overhead. Im Grunde könnte ich das alles in einer DB in verschiedenen Tabellen speichern. Daran wollte ich mich eigentlich vorbeimogeln. Und ich habe ehrlich gesagt noch nie mit DB in Delphi gearbeitet. Ich habe schon ein wenig mit MySQL experimentiert. Ich wollte aber auch ausdrücklich verhindern, dass ich einen Server aufsetzen muss. Gruß, Alex |
AW: Von TStringList abgeleitete Klasse inkl. Objecten speichern
Nun ja, du könntest den Firebird Embedded Server benutzen. Dann brauchst du nichts installieren, nur die gds32.dll mit ausliefern. Und Komponenten für den Zugriff gibts genug. FIBPlus, Zeos usw...
Und so schwer ist DB auch nicht |
AW: Von TStringList abgeleitete Klasse inkl. Objecten speichern
Naja, also deine Objekte sollten schon eine SaveToStream() Methode mitbringen. Dann kannst du in der StringList einfach die SavetoStream() methode überschreiben und jeweils immer die Länge des Namens, den Namen selbst speichern und dann SaveToStream() aufrufen.
Das ist dann zwar ein (unleserliches) Binärformat, und es ist auch ruck-zuck inkompatibel mit Vorgänger/Nachfolgeversionen, aber es geht und nimmt wenig Platz ein :mrgreen: Also TmeineDaten um ein SaveToStream() erweitern (savetofile kannst du der Vollständigkeit halber auch direkt dazutun), und dann sowas machen:
Delphi-Quellcode:
Stream.Write(Items.Count)
for i := 0 to Items.Count - 1 do begin Stream.Write(Items[i].length); Stream.Write(Items[i]); Stream.Write((Objects[i] as TmeineDaten).SaveToStream(Stream)); end; |
AW: Von TStringList abgeleitete Klasse inkl. Objecten speichern
Zitat:
Delphi-Quellcode:
Du musst nur darauf achten, dass alles was gespeichert werden soll im published-Abschnitt steht.
type
{$M+} TMyClass = class(TObject) private FX: integer; published property X: integer read FX write FX; ... end; {$M-} |
AW: Von TStringList abgeleitete Klasse inkl. Objecten speichern
Danke für die Tipps!
So klappt es leider nicht:
Delphi-Quellcode:
Wo ist hier mein Denkfehler?
{$M+}
TDishList= Class(TStringList) // Object für Liste der Gerichte Private Function GetSystemID(Index: Integer): Integer; Procedure SetSystemID(Index: Integer; Const Value: Integer); Public Constructor Create; Destructor Destroy; Override; Function AddDish(Value: TDishes): Integer; Procedure DeleteDish(Index: Integer); Procedure SaveToFile(Const FileName: String); Override; Procedure LoadFromFile(Const FileName: String); Override; Property SystemID[Index: Integer]: Integer Read GetSystemID Write SetSystemID; End; {$M-} // Liste in eine Datei speichern Procedure TDishList.SaveToFile(Const FileName: String); Var I : Integer; Begin If Count < 1 Then Exit; // es muss mind. 1 Eintrag da sein With TMemoryStream.Create Do Try WriteComponent(self); // <- inkompatible Typen SaveToFile(FileName); // alles in der Datei ablegen Finally Free; End; End; Ich kann leider im Public-Bereich nichts vorweisen. Denn das Object selbst ist ja die Liste. Ich hatte auch versucht, mein Object von TComponent abzuleiten und in Public eine TStringList zu deklarieren. Das geht an sich. Beim Speichern bekomme ich aber auch immer nur eine Datei die 17 Bytes groß ist ... Gruß, Alex |
AW: Von TStringList abgeleitete Klasse inkl. Objecten speichern
Ein inherited in savetofile wäre sicher hilfreich
Wie das derzeit ohne Endlosschleife funktioniert, verteh ich nicht. Sobald count >0 ist, ruft savetofile sich selbst doch rekursiv auf, ohne Abbruchbedingung für die Rekursion? Vermutlich löst WriteComponent eine Exception aus, sonst müsste das Programm da abstürzen edit - Blödsinn, in die with Falle gestolpert - solche Konstruktionen gehören veboten :) Mit dem with erreicht man in solchen Fällen wirklich nur, dass man den Code endlos studieren muss, um herauszufinden welche Variable da wo gewitht wird. Aber ohne inherited wird es nicht gehen. |
AW: Von TStringList abgeleitete Klasse inkl. Objecten speichern
Zitat:
Denn mein Object erbt die Methode SaveToFile() abgeleitet von TStringList. Das wiederum speichert ja nur die einzelnen Listeneinträge in einer Datei. Ich kann mir daher nicht vorstellen, dass ein inherited irgend etwas bzgl. der mit der Liste gespeicherten Objecte bewirken soll. Im Moment sieht mein Code - sehr sehr unschön - so aus:
Delphi-Quellcode:
Das laden dann analog. Aber das ist umständlich und kompliziert. Und wenn man das später mal warten will, dann muss man statt 5-7 Zeilen für WriteComponent() eben diese vielen behandeln.
Type
TDish = Record // Record zum abspeichern SystemID : Integer; DishName : String[255]; DishType : Integer; Price : Currency; End; // Liste in eine Datei speichern Procedure TDishList.SaveToFile(Const FileName: String); Var I : Integer; Buffer : TDish; OutFile : File Of TDish; Begin If Count < 1 Then Exit; // es muss mind. 1 Eintrag da sein AssignFile(OutFile, FileName); // Dateiname zuweisen {$I-} FileSetAttr(FileName, $00); // alle Attribute löschen DeleteFile(PAnsiChar(FileName)); // Datei löschen und ReWrite(OutFile); // neu anlegen For I:=0 To Pred(Count) Do // gesamte Liste durchgehen Begin FillChar(Buffer, SizeOf(Buffer), 0); // Puffer leeren Buffer.SystemID:=(Objects[I] As TDishes).SystemID; Buffer.DishName:=(Objects[I] As TDishes).DishName; Buffer.DishType:=(Objects[I] As TDishes).DishType; Buffer.Price:= (Objects[I] As TDishes).Price; Write(OutFile, Buffer); // Daten in Datei ablegen End; CloseFile(OutFile); // die Datei schließen {$I+} End; Gruß, Alex P.S. Ich liebe im Übrigen das With! Wenn ich größere Objecte habe, in denen ich - als welchen Gründe auch immer - bestimmte Properties in der Registry oder sonstwie speichern muss, dann spare ich mir etliche Zeilen Code auf diese Art und Weise. Das ist aber nur meine persönliche Meinung. |
AW: Von TStringList abgeleitete Klasse inkl. Objecten speichern
Gerade bei großen Blöcken kann
Delphi-Quellcode:
aber zu großen Problemen/Seiteneffekten führen
with
|
AW: Von TStringList abgeleitete Klasse inkl. Objecten speichern
Das Problem sind vor allem geschachtelte withs (und die Implementierung einer Methode entspricht schon einem impliziten with). Da sind mir schon Fehler passiert, bei denen ich endlos vor dem Code gesessen bin und gesucht habe, weil ein Bezeichner mit dem falschen with erweitert worden ist, das ist die Einsparung von etwas Codetext nicht wert. Wenn man einem Record oder einer Klasse ein neues Feld (oder Property oder Methode) mit dem gleichen Namen wie in einer anderer Klasse hinzufügt, kann sich das auf irgendwelche geschachtelte withs irgendwo im Code auswirken, an die man überhaupt nicht denkt. Viel Spass.
|
AW: Von TStringList abgeleitete Klasse inkl. Objecten speichern
Moin,
Zitat:
MfG Fabian |
AW: Von TStringList abgeleitete Klasse inkl. Objecten speichern
Ich hab's jetzt noch nicht getestet, aber theoretisch dürfte es funktionieren.
- die Objekte müssen von TComponent abgeleitet sein (ich dachte eigentlich, daß es auch Stream-Lese/Schreib-Methoden gibt, welche direkt TComponent nutzen :gruebel: ) - die Stringliste muß Besitzer (Owner) der Objekte sein - und die verwendeten Objekt-Klassen müssen von Stream.ReadComponent gefunden werden können - ein Vorteil ist, daß es zu einer normalen Stringliste kompatibel sein dürfte, da diese die nachfolgenden Objekte nicht als Text erkannt und damit ignoriert werden. Nur beim Auslesen von UTF-8 könnte es Problemchen geben, da das normale TStrings und Co. seit Delphi 2009, die Datei komplett ausließt, versucht umzukodieren (also inklusive der Objektdaten) und erst dann den Text daraus ausließt ... beim Umkodieren könnten die Objekte allerdings als ungültiges UTF-8 erkannt werden.
Delphi-Quellcode:
Hätte gerne noch die Objekte direkt als TComponent deklariert (nicht als TObject gelassen), aber dieses läßt sich nachträglich leider nicht mehr ändern ordentlich, vorallem wenn man direkt von TStringList ableiten möchte.
type
// must be owns the objects // objects to be derived from TComponent wearer TSavedStringList = class(TStringList) protected procedure PutObject (Index: Integer; AObject: TObject); override; procedure InsertItem(Index: Integer; const S: String; AObject: TObject); override; procedure AddStrings(Strings: TStrings); override; procedure SetOwnsObject(Value: Boolean); public constructor Create; overload; constructor Create(OwnsObjects: Boolean); overload; function AddObject ( const S: String; AObject: TObject): Integer; override; procedure InsertObject (Index: Integer; const S: String; AObject: TObject); override; property OwnsObjects: Boolean write SetOwnsObject; procedure LoadFromStream(Stream: TStream; Encoding: TEncoding); override; procedure SaveToStream (Stream: TStream; Encoding: TEncoding); override; end; procedure TSavedStringList.PutObject(Index: Integer; AObject: TObject); begin if Assigned(AObject) and not (AObject is TComponent) then raise Exception.Create('the objects must be persitent'); inherited; end; procedure TSavedStringList.InsertItem(Index: Integer; const S: String; AObject: TObject); begin if Assigned(AObject) and not (AObject is TComponent) then raise Exception.Create('the objects must be persitent'); inherited; end; procedure TSavedStringList.AddStrings(Strings: TStrings); var S: String; begin BeginUpdate; try for S in Strings do AddObject(S, nil); finally EndUpdate; end; end; procedure TSavedStringList.SetOwnsObject(Value: Boolean); begin if not Value then raise Exception.Create('must be owns the objects'); inherited OwnsObjects := Value; end; constructor TSavedStringList.Create; begin inherited; end; constructor TSavedStringList.Create(OwnsObjects: Boolean); begin if not OwnsObjects then raise Exception.Create('must be owns the objects'); inherited Create; end; function TSavedStringList.AddObject(const S: String; AObject: TObject): Integer; begin if Assigned(AObject) and not (AObject is TComponent) then raise Exception.Create('the objects must be persitent'); inherited; end; procedure TSavedStringList.InsertObject(Index: Integer; const S: String; AObject: TObject); begin if Assigned(AObject) and not (AObject is TComponent) then raise Exception.Create('the objects must be persitent'); inherited; end; procedure TSavedStringList.LoadFromStream(Stream: TStream; Encoding: TEncoding); var Size, i, i2, i3, i4: LongInt; Buffer, EndMarker: TBytes; Data: TStream; begin BeginUpdate; try Size := Stream.Size - Stream.Position; SetLength(Buffer, Size); Stream.ReadBuffer(Buffer[0], Size); Size := TEncoding.GetBufferEncoding(Buffer, Encoding); EndMarker := Encoding.GetBytes(#0); i := Size; i2 := Length(Buffer); if Length(EndMarker) = 1 then begin while i < i2 do if Buffer[i] <> EndMarker[0] then Inc(i) else Break; end else if Length(EndMarker) = 2 then begin Dec(i2); while i < i2 do if PWord(@Buffer[i])^ <> PWord(@EndMarker[0])^ then Inc(i, 2) else Break; end else begin i3 := Length(EndMarker); Dec(i2, i3 - 1); while i < i2 do if not CompareMem(@Buffer[i], @EndMarker[0], i3) then Inc(i, i3) else Break; end; SetTextStr(Encoding.GetString(Buffer, Size, i - Size)); Data := TMemoryStream.Create; try i2 := 0; i3 := Count; Size := Length(Buffer) - 3; while (i < Size) and (i2 < i3) do begin i4 := PLongWord(@Buffer[i])^; inc(i, 4); Data.Size := 0; Data.WriteBuffer(Buffer[i], i4); inc(i, i4); PutObject(i2, Data.ReadComponent(nil)); Inc(i2); end; finally Data.Free; end; finally EndUpdate; end; end; procedure TSavedStringList.SaveToStream(Stream: TStream; Encoding: TEncoding); var EndMarker: TBytes; Data: TStream; i, i2: LongInt; begin inherited; if not Assigned(Encoding) then Encoding := TEncoding.Default; EndMarker := Encoding.GetBytes(#0); Data := TMemoryStream.Create; try Stream.WriteBuffer(EndMarker[0], Length(EndMarker)); for i := 0 to Count - 1 do begin Data.Size := 0; if not (Objects[i] is TComponent) then raise Exception.Create('the objects must be persitent'); Data.WriteComponent(TComponent(Objects[i])); i2 := Data.Size; Stream.WriteBuffer(i2, 4); Stream.CopyFrom(Data, i2); end; finally Data.Free; end; end; Bei Verwendung der Generics, wäre dieses gegangen, aber dann wäre es kein TStrings-Nachfolger mehr. |
AW: Von TStringList abgeleitete Klasse inkl. Objecten speichern
Kleine Frage zu deinem Code, himitsu: Wenn du sowieso nur Nachkommen von TPersistent erlaubst, wieso steht dann überall TObject? Spar dir doch die Manuelle Typ-Prüfung und deklarier den Parameter doch gleich als TPersistent.
[edit]Ah, ich sehe, die Methoden sind als
Delphi-Quellcode:
deklariert. Alles klar.[/edit]
override
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 21:10 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