Ok, zuerst zu den Fehler:
Mein obiges Beispiel ist nicht getestet sondern eben nur ein Beispiel das mal eben reingehämmert habe. Solche Feinheiten sind somit deine Sache
Zum Problem der verlinkten Objekte:
Es gibt mehrere konzeptionelle Ansatzpunkt. Einmal könntest du in TMyBaseClass.SaveToStream() nur den Klassennamen abspeichern. Alle davon abgeleiteten Klasse müsen dann intern in ihrer überschriebenen Methode .SaveToStream() als erstes inherited SaveToStream() aufrufen. Das ist sowieso eine gute Idee. Nachteilig dabei ist der Punkt das wenn wir zb. von TPoint eine TPoint3D ableiten nun im Stream auch der Klassenname von TPoint drinnen stehen würde.
Also wird man mit 2 Methoden arbeiten müssen. .SaveToStream() als statische Methode in TMyBaseClass und .SaveData(Stream) als virtuelle Methode die TPoint etc. pp. überschreiben müssen. TMyBaseClass.SaveToStream() schreibt erstmal den Klassennamen in den Stream und ruft danach .SaveData(Stream) auf. Methode .SaveData() speichert die Daten des jeweiligen Objektes. Sollte diese Klasse zb. TKante sein so ruft sie auch noch FStartPoint.SaveToStream() und FStopPoint.SaveToStream() auf.
Bei .LoadFromStream() muß man dann einige Änderungen machen. Man könte .LoadFromStream() so deklarieren
class function TMyBaseClass.LoadFromStream(Stream: TStream): TMyBaseClass;
Diese Funktion lädt erstmal den Klassennamen aus TStream, erzeugt dann in Result eine Instanz von dieser Klasse und ruft Result.ReadData(Stream) auf. Man hat also nur in TMyBaseClass() eine Klassenmethode .LoadFromStream() und in den davon abgeleiteten Klassen überschreibt man dann .ReadData() um die eigentlichen Daten zu laden. Dein TKante Objet würde dann so arbeiten:
Delphi-Quellcode:
procedure TMyBaseClass.SaveToStream(Stream: TStream);
static;
begin
Stream.WriteString(ClassName);
SaveData(Stream);
end;
procedure TMyBaseClass.SaveData(Stream: TStream);
dynamic;
begin
// nothing todo
end;
class function TMyBaseClass.LoadFromStream(Stream: TStream): TMyBaseClass;
var
Name:
String;
Class: TClass;
begin
Name := Stream.ReadString;
Class := GetClass(
Name);
if Class =
nil then
raise Exception.CreateFmt('
Klasse "%" ist nicht registriert.', [
Name]);
if not (
Class is TMyBaseClass)
then
raise Exception.CreateFmt('
Klasse "%s" ist nicht von TBaseClass abgeleitet', [
Name]);
Result := TMyBaseClass(
Class).Create;
// .Create in TMyBaseClass muß virtuell deklariert sein !!
Result.ReadData(Stream);
end;
procedure TMyBaseClass.ReadData(Stream: TStream);
dynamic;
begin
end;
prcoedure TKante.SaveData(Stream: TStream);
override;
begin
Stream.
Write(MeineDaten,...);
FStartPoint.SaveToStream(Stream);
FStopPoint.SaveToStream(Stream);
end;
procedure TKante.ReadData(Stream: TStream);
override;
begin
Stream.
Read(MeineDaten, ...);
FStartPoint := TMyBaseClass.LoadFromStream(Stream)
as TPoint;
FStopPoint := TMyBaseClass.LoadFromStream(Stream)
as TPoint;
end;
.LoadFromStream() ist dann quasi ein Constructor und der Typcast mit "as TPoint" stellt sicher das keine falsch TMyBaseClass in deinen Feldern landet.
Oben sprach ich meine TNode Objekte an. Diese sind ebenfalls von TObjet abgeleitet. Das bedeutet das wir nun nichtmehr das Streaming-System der
VCL benutzen können wie bei TComponent Klassen gewohnt. Denn diese machen das exakt genauso. Schue dir mal eine
DFM Datei als Text an.
Um denoch meine TNode Objekte so abspeichern zu können wie die
VCL habe ich eine TNodeStreamingComponent von TComponent abgeleitet. Beim Laden und Speichern wird also ein TNode Objekt innerhalb dieser Klasse gekapselt als Property. Das hat den riesigen Vorteil das man in den eigenen Objekten mit published Properties arbeiten kann und das
VCL Streaming System diese Properties speichert und lädt. Das ist so wie bei TFont, TBrush, TPen Objekten die als Properties einer TComponent published wurden. Eine solche Klasse kann ansich nicht mit dem
VCL Streaming gespeichert werden, sondern immer nur als Eigenschaft einer Komponente.
Der Vorteil ist dann:
Delphi-Quellcode:
type
TmyNode = class(Tnode)
private
FX,FY: Integer;
published
property X: Integer read FX write FX;
property Y: Integer read FY write FY;
end;
Das reicht schon aus damit mein TNodeStreamingComponent, das eine TMyNode KLasse in sich kapselt, per
VCL System die beiden Properties X und Y selbständig speichern und laden kann. Man muß also nicht mehr selber ständig diese Properties speichern und laden, sondern die bloße Deklaration als published read/write Properties reicht schon aus.
Ich weis, ich mache dich heis und lasse dich ohne Sourcen im Regen stehen
Allerdings greift mein "Trick" in die Untiefen des
VCL Streamings + TComponnet ein, und das müsstes du dann auch verstanden haben. Hier habe ich leider nicht die Zeit, den Platz und das Recht alles ganz genau aufzuzeigen. (Recht deshalb weil es ein kommerzielles Produkt ist).
Versichern kann ich dir aber das es über diesen Trick definitiv funktioniert. Immerhin der originale Erstsource lief schon in Delphi 1 und wurde mit der Zeit bis nach Delphi 7 portiert, OHNE das es Inkompatibilitäten in den gespeicherten Streams gab. Es geht also.
Gruß hagen