Einzelnen Beitrag anzeigen

Benutzerbild von negaH
negaH

Registriert seit: 25. Jun 2003
Ort: Thüringen
2.950 Beiträge
 
#10

Re: Referzen in ein Stream speichern

  Alt 14. Sep 2006, 23:55
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
  Mit Zitat antworten Zitat