Einzelnen Beitrag anzeigen

Benutzerbild von negaH
negaH

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

Re: Referzen in ein Stream speichern

  Alt 14. Sep 2006, 21:43
Hi,

du kannst das auch selber machen.

Der Aufbau deines Stream sieht immer so aus:

Klassenname als String
Daten zu diesem Objekt
Klassenname als String
Daten zu diesem Objekt

Dh. du speicherst in deinem Stream auch die Klassenname -> Self.ClassName als ShortString -> String der nur 255 Zeichen speichern kann, dafür in String[0] die Länge.

Fehlt noch der Punkt wie man von einem solchen String zurück zu einer Klasse kommt die man instanzieren kann. Dazu gehts du exakt so vor wie die VCL. Diese verwaltet eine TList -> RegisteredClasses und zwei Prozeduren -> RegisterClasses(Classes: array of TClass) und Funktion GetClass(const ClassName: String): TClass;

In deinem Code rufst du nun RegisterClasses([TPoint, TKante, TFlaeche]) in der Initialization Sektion auf.
Deine Stream-Leses-Routine liest erstmal einen ShortString aus dem Stream. Dann wird mit GetClass(NameAusDemStream).Create eine Instance erzeugt. Dieser Instanze übergibst du die Kontrolle des Streams damit sie nun ihre Daten auslesen kann.

Alle deine Klassen sollten von eniner Basisklasse abgeleitet sein, zb. TMyBaseClass.


Delphi-Quellcode:

type
  TMyBaseClass = class
  protected
    procedure LoadFromStream(Stream: TStream); virtual;
  end;
  
  TPoint = class(TMyBaseClass);
  TKante = class(TMyBaseClass);

procedure LoadObjects(List: TList; Stream: TStream);

  function ReadClassName: ShortString;
  begin
    Stream.Read(Result[0], SizeOf(Result[0]));
    Stream.Read(Result[1], Ord(Result[0]));
  end;

var
  Instance: TMyBaseClass;
begin
  while Stream.Position < Stream.Size do
  begin
    Instance := (GetClass(ReadClassName) as TMyBaseClass).Create;
    Instance.LoadFromStream(Stream);
    List.Add(Instance);
  end;
end;

procedure SaveObjects(List: TList; Stream: TStream);
var
  I: Integer;
  S: ShortString;
begin
  for I := 0 to List.Count -1 do
    with List[I] as TMyBaseClass do
    begin
      S := ClassName;
      Stream.Write(S[0], Ord(S[0]) +1);
      SaveToStream(Stream);
    end;
end;
  
initialization
  RegisterClasses([TPoint, TKante, TFlaeche,...]);
end.
Ist natürlich nur ein grobes Beispiel und man kann noch einiges verbessern. Statt zb. den langen Klasssennamen zu speichern köntest du auch zu jeder Klasse eine eigene ID erzeugen und diese dann als Byte,Word oder LongWord abspeichern. Das macht deinen Stream kompakter, besonders falls du tausende solcher Objekte speichern möchtest.

Bei einer meiner Klassen (hierarisch, sortierter Node Baum) mache ich es so das ich als erstes alle Klassennamen der verwendeten Objekte in den Stream speichere. Diese Liste dient nun als Lookuptabelle um einen Klassennamen=Klasse eine ID zu geben. Diese ID ist nichts anderes als der Index in diese Liste. Man speichert also zb. einen Baum aus 10000 Objekten mit 10 unterschiedlichen Klassen so das man als erstes eine sortierte Liste aus Strings=Klassenamen in den Stream speichert. Das sind also 10 Strings. Die nachfolgenden Klassen haben dann eine 1 Byte ID, der Index in diese Liste an der der Klassenname steht.
Somit sparst du viel Speicherplatz, weist von Anfang an welche Klassen im Stream gespeichert sind, und zur Laufzeit kannst du die Klasse einfach in dieser Liste direkt nachschlagen, ein Objeckt wird ja über seinen Byte-Index aus dem Stream identifiziert.

Die Verweise auf dpCollection sind gut gemeint, aber ich meine das du mit wenigen Zeilen Source selber schneller bist und auch was dabei lernst.

Gruß Hagen
  Mit Zitat antworten Zitat