Entwickler-Ecke
Dateizugriff - Verschachtelte Objekte speichern
AXMD - Mo 29.08.05 10:59
Titel: Verschachtelte Objekte speichern
Morgen!
Ich muss programmbedingt ein Objekt speichern, d.h. genauer gesagt eine Klasse, die von TObject abstammt. Das Problem: das Objekt hat zusätzlich zu seinen Eigenschaften auch "Unter"objekte. Wie kann ich das speichern? Und wie wieder laden? WriteComponent TFilestream scheidet aus, weil meine Klassen ja von TObject abstammen. Bin für alle Ideen offen :)
AXMD
Moderiert von
Christian S.: Topic aus CLX / Delphi Language (Object-Pascal) verschoben am Mo 29.08.2005 um 11:27
Christian S. - Mo 29.08.05 11:24
Hallo!
Wie ich Dir schon per ICQ geasgt habe, würde ich das rekursiv machen! Du gibst jedem Objekt eine Methode
SaveToStream(var s : TFilestream). Die sieht dann so aus:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31:
| procedure TaClass.SaveToStream(var s : TFilestream); var i : Integer; begin if s = nil then exit;
s.Write(aVar, sizeOf(aVar)); s.Write(anotherVar, sizeOf(anotherVar));
for i:=0 to High(subObjects) do subObjects[i].SaveToStream(s); end;
procedure TaClass.LoadFromStream(var s : TFilestream); var i : Integer; begin if s = nil then exit;
s.Read(aVar, sizeOf(aVar)); s.Read(anotherVar, sizeOf(anotherVar));
for i:=0 to High(subObjects) do subObjects[i].LoadFromStream(s); end; |
Dadurch, dass jedes Objekt weiß, wievie Daten in Teil 1 stehen (zur Not am Anfang von Teil 1 mitspeichern), weiß es genau, zu welchem Punkt es an die anderen Objekte übergeben muss.
Ist jetzt ohne Delphi zur Hand zu haben geschrieben, aber von der Idee her sollte es gehen.
Grüße
Christian
delfiphan - Mo 29.08.05 14:53
Ich würde eine entsprechende Datenstruktur (z.B. Baum) definieren und die Daten rekursiv dort einfügen. So lässt sich das ganze am Schluss auch als XML Dokument (o.ä.) speichern.
Man muss auch darauf achten, dass man beim Laden die Unterobjekte auch wieder erstellen muss...
Sprint - Mo 29.08.05 15:32
Ich bevorzuge bei sowas immer die Klassen TReader und TWriter. Mal ein Beispiel:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107:
| unit Unit2;
interface
uses SysUtils, Classes;
type TMyClass = class(TObject) private FValueA: String; FValueB: Integer; procedure SaveValues(AFiler: TWriter); procedure LoadValues(AFiler: TReader); public property ValueA: String read FValueA write FValueA; property ValueB: Integer read FValueB write FValueB; end;
TMyObject = class(TObject) private FMyClass: TMyClass; public constructor Create; destructor Destroy; override; procedure SaveToFile(const FileName: String); procedure LoadFromFile(const FileName: String); property MyClass: TMyClass read FMyClass write FMyClass; end;
implementation
procedure TMyClass.LoadValues(AFiler: TReader); begin FValueA := AFiler.ReadString; FValueB := AFiler.ReadInteger; end;
procedure TMyClass.SaveValues(AFiler: TWriter); begin AFiler.WriteString(FValueA); AFiler.WriteInteger(FValueB); end;
constructor TMyObject.Create; begin inherited; FMyClass := TMyClass.Create; end;
destructor TMyObject.Destroy; begin FMyClass.Free; inherited; end;
procedure TMyObject.LoadFromFile(const FileName: String); var Handle: Integer; Stream: THandleStream; Filer: TReader; begin Handle := FileOpen(FileName, fmOpenRead); if Handle <> -1 then begin Stream := THandleStream.Create(Handle); Filer := TReader.Create(Stream, 4096); try Filer.ReadSignature; if Filer.ReadIdent = ClassName then MyClass.LoadValues(Filer); finally Filer.Free; Stream.Free; FileClose(Handle); end; end; end;
procedure TMyObject.SaveToFile(const FileName: String); var Handle: Integer; Stream: THandleStream; Filer: TWriter; begin Handle := FileCreate(FileName); if Handle <> -1 then begin Stream := THandleStream.Create(Handle); Filer := TWriter.Create(Stream, 4096); try Filer.WriteSignature; Filer.WriteIdent(ClassName); FMyClass.SaveValues(Filer); finally Filer.Free; Stream.Free; FileClose(Handle); end; end; end;
end. |
AXMD - Mo 29.08.05 15:38
delfiphan hat folgendes geschrieben: |
Man muss auch darauf achten, dass man beim Laden die Unterobjekte auch wieder erstellen muss... |
Eben das wird eines des größeren Probleme werden. Vor allem deshalb, weil 4 Kinderklassen von meiner Klasse abstammen, die alle "Unter"objekte sein können.
AXMD
Christian S. - Mo 29.08.05 15:53
AXMD hat folgendes geschrieben: |
Eben das wird eines des größeren Probleme werden. Vor allem deshalb, weil 4 Kinderklassen von meiner Klasse abstammen, die alle "Unter"objekte sein können. |
Um das hinzubekommen, könnte Dir
dies [
http://www.christian-stelzmann.de/artikel/klassen_registrieren.htm] helfen. Da instanziert eine Mutterklasse beim Laden von Klassen aus einer Datei immer die richtige Kindklasse. Arbeitet auch ganz gut mit dem zusammen, was ich oben geschrieben habe ;-)
AXMD - Mo 29.08.05 18:22
Konnte das Problem dank Christians Ratschlägen und ein bisschen Experimentieren gut lösen :). Für alle die's interessiert: der Code hier. Im Anhang die komplette Unit um die Sache im Kontext zu sehen :)
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| procedure TCircuitElementGroup.SaveToStream(var AStream: TFileStream); var i: Integer; begin inherited; i := Length(FElements); AStream.Write(i, SizeOf(i)); AStream.Write(FParallel, SizeOf(FParallel)); for i := Low(FElements) to High(FElements) do FElements[i].SaveToStream(AStream); end; |
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28:
| function LoadFromStream(var AStream: TFileStream): TCustomCircuitElement; var TempID, i: Integer; AValue: Double; AName: ShortString; AParallel: Boolean; begin Result := nil; with AStream do begin Read(TempID, SizeOf(TempID)); Read(AName, SizeOf(AName)); Read(AValue, SizeOf(AValue)); case TempID of 1: Result := TResistor.Create(AValue); 2: Result := TCapacitor.Create(AValue); 3: Result := TCoil.Create(AValue); 4: begin Result := TCircuitElementGroup.Create; Read(TempID, SizeOf(TempID)); Read(AParallel, SizeOf(AParallel)); (Result as TCircuitElementGroup).Parallel := AParallel; for i := 0 to TempID - 1 do (Result as TCircuitElementGroup).AddElement(LoadFromStream(AStream)); end; end; Result.Name := AName; end; end; |
AXMD
//EDIT: Datei angehängt :)
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!