![]() |
Referzen in ein Stream speichern
Hallo zusammen
Ich habe wieder einmal eine Frage für die Expterten hier. Wie der Titel schon sagt, möchte ich Referenzen von Objekten in ein Stream speichern. Geht das? Falls ja, bitte wie? Zum besseren Verständnis des Problems habe ich ein Programmgerüst der Objekte erstellt die gespeichert werden sollen. TPoint ist ein Punkt mit zwei Koordinaten TKante stellt die Verbingung zwischen zwei Punkten dar Daraus entsteht schliessliche ein Netz oder ein Graph der gespeichert werden soll.
Delphi-Quellcode:
Vielen Dank für Eure TippsTPoint = Class mx,my:Integer; constructor Create(x1,y1:Integer); Constructor Save(S:Stream); Constructor Load(s:TStream); End; TKante = Class mP1,mP2:TPoint; constructor Create(P1,P2:TPoint); Constructor Save(S:Stream); Constructor Load(s:TStream); End; TGraph = class PunkteListe:TList; KantenList:TList; constructor Create(); end; constructor TPoint.create(x1,y1:Integer); Begin mx:=x1; my:=y1; End; Constructor TPoint.Save(S:Stream); Begin s.Write(mx,sizeof(mx)); s.Write(my,sizeof(my)); End; Constructor TPoint.Load(s:TStream); Begin s.read(mx,sizeof(mx)); s.read(my,sizeof(my)); End; constructor TKante.create(P1,P2:TPoint); Begin mP1:=P1; mP2:=P2; End; Constructor TKante.Save(S:Stream); Begin ??? End; Constructor TKante.Load(s:TStream); Begin ??? End; constructor TGraph.Create(); var P1,P2:TPoint; K:TKante; Begin PunkteListe:=TList.Create; // erzeuge 10 zufällige Punkte for i:=0 TO 9 Do Begin P:=TPoint.create(Random(100),Random(100)); PunkteListe.Add(p); End; Erzeuge 4 zufällige Kanten For j:=0 TO 4 Do Begin Kantenliste:=TKante.Create(l[Random(9)],L[Random(9)); End; End; constructor TGraph.LoadFromstream(); Begin // lade Punkte ?? // lade Kanten ?? End; Geri |
DP-Maintenance
Dieses Thema wurde von "Dax" von "Open-Source" nach "Sonstige Fragen zu Delphi" verschoben.
Das ist kein OS-Code.. |
Re: Referzen in ein Stream speichern
Das bezeichnet man als Objektpersistenz.
Da gibt es die dpCollection (mal suchen im Forum) und JEDI Obiwan, ein Objektpersistenzframework. |
Re: Referzen in ein Stream speichern
Hallo Manuel
Vielen Dank für den hilfreichen Hinweis. Ich habe mir dpCollection nun einmal herunter geladen. Mir ist aber einfach nicht klar wie ich sie auf meinen Fall anwenden muss. Hast du hieb bitte evtl. auch noch so einen guten Tipp, oder ein einfaches Beispiel in dem die Technik gezeigt wird? Vielen Dank nochmals Geri |
Re: Referzen in ein Stream speichern
Nein, ich habe sie noch nie verwendet. Einfach weiter suchen.
|
Re: Referzen in ein Stream speichern
Damit wird das ganze ganz schnell ganz einfach:
![]() |
Re: Referzen in ein Stream speichern
Hallo Benjamin
Vielen Dank für Deine Mühe. Auf der Suche habe ich auch noch diesen Link gefunden: ![]() Ganz schlau bin ich aber trotzdem noch nicht geworden. Insbesondere weil ich Einträge in einem Tlist-Objekt speichern möchte. Beste Grüsse Geri |
Re: Referzen in ein Stream speichern
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:
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.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. 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 |
Re: Referzen in ein Stream speichern
Hallo Hagen
Vielen Dank für Dein tolles (kurzes und sehr gut nachvollziehbares) Beispiel und die detaillierte Beschreibung!! Super! Ich denke, nun ist mir klar, wie ich eine TListe speicher. Wie schreibe ich aber das Objekt Kante in ein Stream. Sind ja nur die Pointer auf die Punkte gespeichert, die in einer anderen Liste gespeichert sind.
Delphi-Quellcode:
Wenn ich Deinen Code compiliere, dann erhalte ich ein paar Meldungen
TGraph = class
PunkteListe:TList; KantenList:TList; constructor Create(); end; Procedure TPoint.Store(s:TStream) Begin S.Write(x, SizeOf(x)); S.Write(y, SizeOf(y)); End; Procedure TPoint.Load(s:TStream) Begin S.Read(x, SizeOf(x)); S.Read(y, SizeOf(y)); End; Procedure TPoint.Load(s:TStream) Begin S.Read(x, SizeOf(x)); S.Read(y, SizeOf(y)); End; Procedure TKante.Store(s:TStream) Begin S.Write( ...schreibe Referenz von P1 in das Stream??? S.Write( ...schreibe Referenz von P2 in das Stream??? End; Procedure TKante.Store(s:TStream) Begin S.Read( ...lies Referenz von P1 in das Stream??? S.Read( ...lies Referenz von P2 in das Stream??? End; constructor TGraph.Create(); var P1,P2:TPoint; K:TKante; Begin PunkteListe:=TList.Create; // erzeuge 10 zufällige Punkte for i:=0 TO 9 Do Begin P:=TPoint.create(Random(100),Random(100)); PunkteListe.Add(p); End; // Erzeuge 4 zufällige Kanten For j:=0 TO 4 Do Begin Kantenliste:=TKante.Create(l[Random(9)],L[Random(9)); End; End;
Delphi-Quellcode:
Vielen Dank nochmals und beste Grüsse
...
Instance := (GetClass(ReadClassName) as TMyBaseClass).Create; //[Fehler] uHagen.pas(50): E2015 Operator ist auf diesen Operandentyp nicht anwendbar ... with List[I] as TMyBaseClass do // Operator nicht anwendbar SaveToStream(Stream); // wennn ich richtig verstanden habe, dann werden hier z.B. die Koordinaten für einen Punkt gespeichert. Wie speichere ich aber bitte die Kantenliste? Geri |
Re: Referzen in ein Stream speichern
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
Delphi-Quellcode:
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:
class function TMyBaseClass.LoadFromStream(Stream: TStream): TMyBaseClass;
Delphi-Quellcode:
.LoadFromStream() ist dann quasi ein Constructor und der Typcast mit "as TPoint" stellt sicher das keine falsch TMyBaseClass in deinen Feldern landet.
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; 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:
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.
type
TmyNode = class(Tnode) private FX,FY: Integer; published property X: Integer read FX write FX; property Y: Integer read FY write FY; end; 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 |
Re: Referzen in ein Stream speichern
Hallo Hagen
Vielen Dank nochmals für die Darstellung der Situation und Lösungsansatz. Dein Ansatz mit den Nodes sieht auch interessant aus. Wie sieht hier aber bitte das Basisobjekt Tnode aus? However, mal schauen ob ich her weiter komme... Früher, als es noch die VMT gab, war das jedenfalls noch um einiges einfacher. Beste Grüsse und herzlichen Dank Geri |
Re: Referzen in ein Stream speichern
Sorry Geri, aber die VMT gibts noch heute, muß es auch denn sonst gäbe es keine Methoden die als "virtual" deklariert sind. Zudem gibts noch die DMT=dynamic Method Table eingeführt in Borland Pascal 7. Und dann noch die RTTI. All das gibts seit mindestens Delphi 1, die DMT seit BP7 und die VMT seit BP5.
Den Source zu meinen TNode kann ich leider nicht veröffentlichen. Ist auch nicht soooo wichtig da es im Grunde auch nur von TObject abgeleitet wurde, eine stinknormale Klasse eben. Entscheidend ist der Prozess beim Speichern und Laden einer solchen Klasse die nicht von TComponent oder TPersistent abgeleitet wurde. Warum Borland sein VCL Streaming System erst mit TComponent eingebaut hat und warum es keine TObject Klassen abspeichern kann ist mir ebenfalls ein Rätsel. Auch hätte Borland ohne Probleme auf den Notbehelf mit RegisterClasses() und GetClass() komplett verzichten können. Wie ich hier im Forum schon gezeigt habe (Thread RTII enumerieren in der CodeLib) kann man durchaus über die RTTI aller Module iterieren und somit auch zu einem Klassennamen die Klasse ermitteln. Die Klasse -> TClass, TObject usw. ist nämlich nichts anderes als ein Zeiger in das Codesegement desjenigen Modules das die Unit enthält in dem diese Klasse deklariert wurde und zeigt direkt in die RTTI=Run Time Type Information dieser Klasse. In dieser RTTI enthalten ist dann auch die VMT, DMT, InterfaceTable,Classname,ParentClass,Destructor,Co nstructor,InstanceSize,GUID Tabelle usw. einer Klasse. Ich meine das du dich näher mit meinem letzten Vorschlag mit .LoadFromStream() -> .ReadData() und .SaveToStream() -> WriteData() beschäftigen solltest. Das funktioniert, ist recht simpel und auch relativ flexibel. Der einzigste Vorteil des VCL Streamings wäre das man 1.) mit ObjectBinaryToText() einen solchen Stream aus dem binären Format in ein Textformat konvertieren kann, siehe DFMs. Somit ist der Streaminhalt direkt editierbar wenn man weis was man tut. Man kann diesen Text dann auch wieder zurück ins binäre Format konvertieren, was Speicherplatz spart. 2.) in gewissen Grenzen ist das VCL Format updatebar. Dh. man kann zu einem späteren Zeitpunkt in einer existenten Klasse neue Properties deklarieren und denoch alte Stream einlesen ohne Probleme. Diese neuen Properties sind dann einfach auf dem default Wert eingelstellt und werden ansich ignoriert. Das Entfernen einer solchen Property ist aber gefährlich. Denn lädt man ein alten Stream mit dieser alten nun entfernten Property dann krachts. Aber immerhin kann man diese Objekte in Zukunft noch erweitern. 3.) das Streamingsystem enthält alles um alle Standard Datentypen wie Integer, String, TPersistent Klassen die als published Proeprties deklariert wurden, zu speichern und zu laden. Man muß also keinerlei Code mehr schreiben der das macht. 4.) das das Streamingsystem nicht nur den Wert einer Property abspeichert sondern auch den Namen dieser Property (zb. eben X=7 Y=18) ist die Reihenfolge in der die Properties zu einer Klasse im Stream liegen irrelevant. All diese Punkte waren ein k.o. Kriterium für die Entscheidung das auch in meinen TNode Klassen benutzen zu wollen. Gruß Hagen |
Re: Referzen in ein Stream speichern
Hallo Hagen
Vielen Dank für Dene detaillierten Erläuterungen zu den Konzepten. Ich habe inschwischen ein kleines Progrämmchen erstellt und denke, ich bin fast am Ziel, aber es funktioniert noch nicht. Nach dem Drücken des Knopfes werden zuerst 10 Punkte und anschliessend fünf Kanten erzeugt. Beim lesen aus dem Stream scheitert das Programm aber an der Stelle wo: if myClass = nil then raise Exception.CreateFmt('Klasse "%" ist nicht registriert.', [Name]); steht. Die speziellen Schlüsselwöter dynamic und static habe ich weggelassen. RegisterClasses([TPoint, TKante]); habe ich auch auskommentiert Wäre nett von Dir, wenn du bitte mal mit Deinem geschulten Auge den Code ansehen würdest. Es ist ein minimales "lauffähiges" Programm. Gebe für heute auf, vielen Dank, gute Nachte und beste Grüsse Geri |
Re: Referzen in ein Stream speichern
Wenn du RegisterClasses() weglässt so kann GetClass() nichts finden -> Class == nil;
Das Problem ist doch folgendes: Eine TClass ist ein Zeiger in das CodeSegement. Da wir aber immer weiter am Source rumbasteln und alles neu kompilieren wird also der Zeiger dieser Klasse ins Codesegement abhängig von unserem Source, dem Compiler dem Linker und sogar vom Betriebstsystem sein das unseren Code in den Speicher lädt. Der Zeiger auf eine Klasse ist also nur zur Laufzeit gültig und kann von Laufzeit zu Laufzeit einen anderen Wert annehmen. Ergo: ein direktes Speichern dieses Klassenzeigers in einen externen Stream fällt flach da er beim nächsten Laden ins Nirwana zeigen könnte (höchst wahrscheinlich). Wir müssen also einen anderen eindeutigen Wert im Stream abspeichern. Nun Klassennamen müssen in Delphi eindeutig sein, zumindestens innerhalb einer Unit. Damit wir aber zu einem Klassennamen die Klasse bekommen müssen wir diese Klasse zu Laufzeit irgendwo registrieren (normalerweise wenn wir ohne Tricks arbeiten wollen es geht nämlich auch ohne) Borland benutzt dazu RegisterClasses(). Alle übergebenen Klassen werden in einer globalen TList gespeichert, wir wissen ja das eine Klasse auch nur ein Pointer ist und somit in eine TList passt. Sucht man nun mit GetClass(AClassName):TClass in dieser Liste so geht man jeden Eintrag in der List durch und vergleicht mit AnsiCompareText(TClass(List[i]).ClassName, AClassName) diesen Namen. Sollte er übeeinstimmen haben wir zu einem Klassennamen als String die zugehrige Klasse und damit den zur Laufzeit gültigen Zeiger auf die Klasse gefunden. Falls du die Sourcen von Classes.pas hast solltest du dir mal beide Funktionen anschauen. Gruß Hagen |
Re: Referzen in ein Stream speichern
Hallo Hagen
Na du bist ein Nachtschwärmer. Vielen Dank für Deine schnelle Rückmeldung. Ich kann das mit den Pointer im Codesegment gut nachvollziehen. Allerdings lässt sich das Programm nicht compilieren, wenn TBaseClass von TObject abgeleitet ist. Den Source habe ich von Classes.pas leider nicht. Ich habe aber gesehen, dass RegisterClasses nur Klassen übernimmt, die von TPersistentClass abegleietet sind. Ich ware aber eben der Meinung, so. Beste Grüsse und vilen Dank für Deine grosse Mühe! Geri |
Re: Referzen in ein Stream speichern
Hm, leider habe ich nicht alle Sourcezeilen der VCL im Kopf ;)
Drei Möglichkeiten: 1.) leite deine Basisklasse auch von TPersistent ab TPersistentClass ist deklariert als TPersistentClass = class of TPersistent. Vorteil! ist das TPersistent schon den Zuweisungsmechanismus gleicher Instanzen integriert hat, also die vrtuellen Methoden .Assign() und .AssignTo(). Das dürftest du für deine TPoint/TKante Objekte ja ebenfals benötigen um zb. ein TPoint Objekt A mit A.Assign(B) auf die Werte von B zu setzen. Damit würdest du also VCL konform bleiben in deinem Design. 2.) kümmer dich nicht um TPersistentClass und caste deine Klassen hart nach TPersistent. Ein "unschöner" weg aber sauber lauffähig bisher. Denn intern ist es RegisterClasses() und GetClass() ziemlich schnuppe was für eine Klasse sie bekommen, hauptsache abgeleitet von TObject. 3.) baue deine eigene RegisterClasses() und GetClasses(). Das habe ich oben ja schon fast vollständig erklärt gehabt -> maximal 15 Zeilen Source sind nötig. Ich würde in deinem Falle 1.) und 3.) machen. 1.) um mehr VCL konforme Funktionen drinnen zu haben. Ein TPersistent Nachfahre ist zb. auch TBrush, TFont, TPen und kann somit auch als Property einer Komponnete benutzt werden. Das wäre für deine TPoint/TKante Klassen ja eventuell ebenfalls interessant. Und 3.) um einfach von der VCL Fnktionen wegzukommen, unabhängiger und gezielter zu sein. Immerhin teilen sich viele Dutzende von Units die Funktionalität von RegisterClasses()/GetClass() und ein Klassenname muß nur innerhalb einer Unit eindeutig sein. Es ist also durchaus möglich zwei Klassen mit gleichem Namen zu deklarieren und beide in RegisterClasses() registrieren zu wollen. Das geht natürlich nicht und RegisterClasses() würde eine Exception auslösen. Baust du diese Funktionalität selber für deine TMyBaseClass nach so bist du auf der sicheren Seite. Gruß Hagen [edit] ganz vergessen, dein Basistyp solllte inetwa so ausssehen:
Delphi-Quellcode:
Entscheidend ist TBaseClass = class of TBase. Du definierst also einen Metaklassen-Datentyp um später mit polymorphen Klassentypen arbeiten zu können. Wir können also nicht nur OOP anwenden um Objekt polymorph == typkompatbel zu machen, sondern eben auch mit Klassen. Eine Variable die vom Basistyp her eine Klasse abgeleitet von TBase enthalten soll muß dann als TBaseClass deklariert werden. Schätze das war der Fehler warum du oben Porbleme bekommen hast (Operator nicht anwendbar, oder so) und ich hatte das bisher nicht explizit erwähnt (dachte du weist das :) )
type
TBase = class; TBaseClass = class of TBase; TPoint = class(TBase) TKante = class(TBase) procedure RegisterMyClasses(const AClasses: array of TBaseClass); function GetMyClass(const AClassName: String): TBaseClass; [/edit] |
Re: Referzen in ein Stream speichern
Hallo Hagen
Vielen Dank für Deine Rückmeldung. Ich hoffe, du hast nach der langen Nach gut geschlafen:) Wenn ich den Code betrachte, dann habe ich mehr und mehr das Gefühl, dass ich das in kurzer Zeit ohne lauffähiges Beispiel nicht umsetzen kann. Auf dieser Ebene bin ich eher Anwender und so wie es aussieht, bedarf es für diese Aufgabe scheinbar mehr Background-Wissen. Was
Delphi-Quellcode:
macht das kann ich mir vorstellen. Hier wird ein Klassenname übergeben und ich liefer den Type des entsprechenden Objektes zurück. Also z.B.
function GetMyClass(const AClassName: String): TBaseClass;
Delphi-Quellcode:
Bzgl. Ableiten von TPersistent:
function GetMyClass(const AClassName: String): TBaseClass;
Begin if AClassName = 'TPOINT' then result:= TPOINT else if AClassName = 'TKANTE' then result:= TKANTE else result:=nil; End;
Delphi-Quellcode:
TBase is ja bei die hier auch nicht von TPersistent abeleitet..?
type
TBase = class; TBaseClass = class of TBase; TPoint = class(TBase) TKante = class(TBase) Bei mir sehen die Klassen aktuell folgendermassen aus.
Delphi-Quellcode:
wie ich nun
type
TMyBaseClass = class public Constructor Create();virtual; class function LoadFromStream(Stream: TStream):TMyBaseClass; virtual; procedure SaveToStream(Stream: TStream); procedure Write(Stream: TStream); dynamic; procedure Read(Stream: TStream); dynamic; end; TPoint = class(TMyBaseClass) public x,y:Integer; Constructor Create(mx,my:Integer);overload;virtual; Constructor Create();overload;override; procedure Write(S:TStream);override; procedure Read(S:TStream);override; end; TKante = class(TMyBaseClass) public P1,P2:TPoint; procedure Write(Stream: TStream);override; procedure Read(Stream: TStream);override; constructor Create(mP1,mP2:TPoint);overload;virtual; Constructor Create();overload; override; end;
Delphi-Quellcode:
einbauen muss..???:)
TBase = class;
TBaseClass = class of TBase; Bzgl.
Delphi-Quellcode:
..ist mit auch nicht ganz klar was die macht.
procedure RegisterMyClasses(const AClasses: array of TBaseClass);
Tja, ich komme so nicht weiter. Ich schaue mal weiter im Internet nach einem lauffähigen Beispiel. Vielleicht werde ich fündig. Jedenfalls nochmals vielen Dank, dass du mir hier so nachhaltig weiter geholfen hast! Nun kenne ich mal die Richtung... Geri |
Re: Referzen in ein Stream speichern
Ok, mal ein kleines Beispiel, mehr geht aber wirklich nicht:
Delphi-Quellcode:
So wie immer keine Gewähr da ich das alles aus dem Stegreif geschrieben habe ohne es jetzt in Delphi zu testen.
type
TBase = class(TPersistent) protected procedure ReadData(Stream: TStream); dynamic; abstract; procedure WriteData(Stream: TStream); dynamic; abstract; procedure Changed; virtual; public class function LoadFromStream(Stream: TStream): TBase; procedure SaveToStream(Stream: TStream); end; TBaseClass = class of TBase; TPoint = class(TBase) private FX: Integer; FY: Integer; procedure SetX(Value: Integer); procedure SetY(Value: Integer); protected procedure ReadData(Stream: TStream); override; procedure WriteData(Stream: TStream); override; public constructor Create(APoint: TPoint); reintoduce; overload; constructor Create(AX, AY: Integer); reintruduce; overload; procedure Assign(Source: TPersistent); override; published property X: Integer read FX write SetX; property Y: Integer read FY write SetY; end; TKante = class(TBase) private FPoints: array[0..1] of TPoint; function GetPoint(Index: Integer): TPoint; procedure SetPoint(Index: Integer; Value: TPoint); protected procedure ReadData(Stream: TStream); override; procedure WriteData(Stream: TStream); override; public constructor Create(AKante: TKante); reintroduce; overload; constructor Create(AStart, AStop: TPoint); reintroduce; overload; constructor Create(AStartX, AStartY, AStopX, AStopY: Integer); reintroduce; overload; destructor Destroy; override; procedure Assign(Source: TPersistent); override; published property Start: TPoint inxex 0 read GetPoint write SetPoint; property Stop: TPoint index 1 read GetPoint write SetPoint; end; procedure RegisterBaseClasses(const AClasses: array of TBaseClass); function GetBaseClass(const AClassName: String): TBaseClass; procedure SaveList(List: TList; Stream: TStream); // besser ist es eine eigene TList abzuleiten die nur TBaseClass enthalten kann procedure LoadList(List: TList; Stream: TStream); implementation var FClassList: TList = nil; procedure RegisterBaseClasses(const AClasses: array of TBaseClass); var I: Integer; begin Assert(FClassList <> nil); for I := Low(AClasses) to High(AClasses) do if not (AClasses[I] is TBaseClass) then raise Exception.Create('ungültige Klasse in RegisterBaseClasses') else if GetBaseClass(AClasses[I].ClassName) <> nil then raise Exception.CreateFmt('eine Klasse mit Namen "%s" ist schon registriert', [AClasses[I].ClassName]) else FClassList.Add(AClasses[I]); end; function GetBaseClass(const AClassName: String): TBaseClass; var I: Integer; begin Assert(FClassList <> nil); Result := nil; for I := 0 to FClassList.Count -1 do if AnsiCompareText(AClassName, TClass(FClassList[I]).ClassName) = 0 then begin Result := FClassList[I]; Break; end; end; procedure SaveList(List: TList; Stream: TStream); var I: Integer; begin for I := 0 to List.Count -1 do TBase(List[I]).SaveToStream(Stream); end; procedure LoadList(List: TList; Stream: TStream); begin while Stream.Position < Stream.Size do List.Add(TBase.LoadFromStream(Stream)); end; // .TBase procedure TBase.Changed; begin // hier zb. ein NotifyEvent einbauen end; class function TBase.LoadFromStream(Stream: TStream): TBase; var NewClass: TBaseClass; NewName: ShortString; begin Stream.Read(NewName[0], 1); Stream.Read(NewName[1], Ord(NewName[0])); NewClass := GetBaseClass(NewName); if NewClass = nil then raise Exception.CreateFmt('Klasse "%s" ist nicht registriert', [NewName]); Result := NewClass.Create; Result.ReadData(Stream); end; procedure TBase.SaveToStream(Stream: TStream); var NewName: ShortString; begin NewName := ClassName; Stream.Write(NewName[0], Ord(NewName[0]) +1); WriteData(Stream); end; // .TPoint procedure TPoint.SetX(Value: Integer); begin if Value <> FX then begin FX := Value; Changed; end; end; procedure TPoint.SetY(Value: Integer); begin if Value <> FY then begin FY := Value; Changed; end; end; procedure TPoint.ReadData(Stream: TStream); begin Stream.Read(FX, SizeOf(FX)); Stream.Read(FY, SizeOf(FY)); end; procedure TPoint.WriteData(Stream: TStream); begin Stream.Write(FX, SizeOf(FX)); Stream.Write(FY, SizeOf(FY)); end; constructor TPoint.Create(APoint: TPoint); begin inherited Create; Assign(APoint); end; constructor TPoint.Create(AX, AY: Integer); begin inherited Create; X := AX; Y := AY; end; procedure TPoint.Assign(Source: TPersistent); var S: TPoint absolute Source; begin if Source is TPoint then begin if (FX <> S.FX) or (FY <> S.Y) then begin FX := S.FX; FY := S.FY; Changed; end; end else if Source = nil then // bedeutet TPoint(nil) == TPoint(0,0) und ist eine Definitionssache des Programmierers begin if FX or FY <> 0 then // effizienter! als if (FX <> 0) or (FY <> 0) then begin FX := 0; FY := 0; Changed; end; end else inherited Assign(Source); end; // .TKante function TKante.GetPoint(Index: Integer): TPoint; begin if FPoints[Index] = nil then FPoints[Index] := TPoint.Create; // Auto-Allokation beim Zugriff auf Start oder Stop Result := FPoints[Index]; end; procedure TKante.SetPoint(Index: Integer; Value: TPoint); begin GetPoint(Index).Assign(Value); // WICHTIG! niemals ein Object setzen sondern immer dessen EIgenschaften kopieren end; procedure TKante.ReadData(Stream: TStream); begin // hier gibt es 2 Möglichkeiten // 1. die TPoint aus Stream als Objekte laden also NEU erzeugen FreeAndNil(FPoints[0]); FreeAndNil(FPoints[1]); FPoints[0] := TPoint.LoadFromStream(Stream) as TPoint; FPoints[1] := TPoint.LoadFromStream(Stream) as TPoint; // 2. nur die Koordinaten der Points laden Start.ReadData(Stream); Stop.ReadData(Stream); // Vorteil: wir sparen die beiden String "TPoint" im Stream // Nachteil: die beien Punkte Start,Stop müssen IMMER vom Typ TPoint sein // eine TKante mit 3D Koordinaten könnte von TKante abgeleitet sein aber statt TPoint dann TPoint3D benutzen. // Diese TPoint3D Klasse hätte also X,Y,Z // Für eine der beiden Methoden musst du dich entscheiden end; procedure TKante.WriteData(Stream: TStream); begin // 1. Methode, Klasse mit Daten Start.SaveToStream(Stream); Stop.SaveToStream(Stream); // 2. Methode, nur Daten Start.SaveData(Stream); Stop.SaveData(Stream); end; procedure TKante.Assign(Source: TPersistent); begin if Source is TKante then begin Start.Assign(TKante(Source).Start); Stop.Assign(TKante(Source).Stop); Changed; end else if Source = nil then begin FreeAndNil(FPoints[0]); FreeAndNil(FPoints[1]); Changed; end else inherited Assign(Source); end; constructor TKante.Create(AStart,AStop: TPoint); begin inherited Create; Start := AStart; Stop := AStop; end; constructor TKante.Create(AStartX,AStartY,AStopX,AStopY: Integer); begin inherited Create; Start.X := AStartX; Start.Y := AStartY; Stop.X := AStopX; Stop.Y := AStopY; end; constructor TKante.Create(AKante: TKante); begin inherited Create; Assign(AKante); end; destructor TKante.Destroy; begin FreeAndNil(FPoints[0]); FreeAndNil(FPoints[1]); inherited Destroy; end; initialization FClassList := TList.Create; RegisterBaseClasses([TPoint, TKante]); finalization FreeAndNil(FClassList); end. Gruß Hagen |
Re: Referzen in ein Stream speichern
Hallo Hagen
Du bist wirklich spitze!! Vielen Dank! Ich glaube, nun kratze ich doch nochmal die Kurve.:) Der code liess sich mit zwei Ausnahmen compilieren:) einmal ein Tippfehler und einmal die Meldung [Fehler] uHagen2.pas(70): E2015 Operator ist auf diesen Operandentyp nicht anwendbar im folgenden COde bei "if not (AClasses[I] is TBaseClass) then"
Delphi-Quellcode:
Deshalb habe ich den Teil ausser "FClassList.Add(AClasses[I]);" ausgeklammertprocedure RegisterBaseClasses(const AClasses: array of TBaseClass); var I: Integer; begin Assert(FClassList <> nil); for I := Low(AClasses) to High(AClasses) do if not (AClasses[I] is TBaseClass) then raise Exception.Create('ungültige Klasse in RegisterBaseClasses') else if GetBaseClass(AClasses[I].ClassName) <> nil then raise Exception.CreateFmt('eine Klasse mit Namen "%s" ist schon registriert', [AClasses[I].ClassName]) else FClassList.Add(AClasses[I]); end; Dann habe ich eine Beispiel geschrieben. Es werden 10 Punkte und fünf Kanten erzeugt. Das Schreiben und das Lesen der Punkte klappt. Nur sind in der Liste nach dem Auslen 15 Punkte drin. Die restlichen 5 Punkte haben deshalb nicht definierte Werte. Hmmm?? Die Katenliste ist nach dem Laden auch leer. KantenListe.count = 0. Hast du bitte vielleicht noch eine Idee an was es liegen könnte.
Delphi-Quellcode:
Beste Grüsse und vielen Dank
procedure TForm6.Button1Click(Sender: TObject);
var i:Byte; P:TPoint; K:TKante; Begin PunkteListe:=TList.Create; KantenListe:=TList.Create; // erzeuge 10 zufällige Punkte for i:=0 TO 9 Do Begin P:=TPoint.create(); P.X:=i; P.y:=i; memo1.lines.Add('Init Punkt ' + IntToStr(i) + ' ' + IntToStr(P.x) + ' ' + IntToStr(P.y)); PunkteListe.Add(p); End; // Erzeuge 4 zufällige Kanten For i:=0 TO 4 Do Begin K:=TKante.Create(); K.Start:=PunkteListe[Random(9)]; K.Stop:=PunkteListe[Random(9)]; memo1.lines.Add('Init Kante ' + IntToStr(i)); KantenListe.Add(k); End; SaveToFile('C:\test.txt'); // schreibe Punkte und Katen in ein File PunkteListe.Clear; KantenListe.Clear; LoadFromFile('C:\test.txt'); // lade Punkte und Katen aus einem File //------------ zeige Punkte -------------------- For i:=0 TO PunkteListe.count-1 Do Begin P:=PunkteListe[i]; memo1.lines.Add('Gelesen Punkt ' + IntToStr(i) + ' ' + IntToStr(P.x) + ' ' + IntToStr(P.y)); end; //------------ zeige Kanten -------------------- For i:=0 TO KantenListe.count-1 Do Begin K:=KantenListe[i]; memo1.lines.Add('Gelesen Kante ' + IntToStr(i) + ' ' + IntToStr(K.Start.x) + ' ' + IntToStr(K.Start.y)); end; end; Geri PS: Könntest doch mal vieleicht ein Buch über das Thema schreiben...:) Reich wird man davon nicht, aber es macht auch Spass. Wenn Deine Beiträge alle so lange sind, dann liegt der Inhalt ja schon vor:) |
Re: Referzen in ein Stream speichern
Hallo Hagen
Ups, ich habe gesehen, dass du noch ein paar Änderungen gemacht hast. Mit dem neueren Code (reintroduce) erhalte ich gleich zu Beginn die Meldung, dass die Klasse nicht registriert sei und das Programm endet mit einem Laufzeitfehler. Das war zuvor noch etwas besser:) Beste Grüsse Geri |
Re: Referzen in ein Stream speichern
Tja, hilft wohl nichts man sollte immer auch einen Test des Sources machen. Leider kann ich mir eben nicht ALLES merken und so ist es auch logisch das ein eben mal schnell gehackter Code niemals auf Anhieb funktionieren wird.
Ich werde es also mal selber testen müssen, die Frage ist wann habe ich Zeit dazu :( Gruß Hagen |
Re: Referzen in ein Stream speichern
Delphi-Quellcode:
Mit wenigen Änderungen absolut lauffähig :)
unit Unit2;
interface uses SysUtils, Classes; type TBase = class(TPersistent) protected procedure ReadData(Stream: TStream); dynamic; abstract; procedure WriteData(Stream: TStream); dynamic; abstract; procedure Changed; virtual; public class function LoadFromStream(Stream: TStream): TBase; procedure SaveToStream(Stream: TStream); end; TBaseClass = class of TBase; TPoint = class(TBase) private FX: Integer; FY: Integer; procedure SetX(Value: Integer); procedure SetY(Value: Integer); protected procedure ReadData(Stream: TStream); override; procedure WriteData(Stream: TStream); override; public constructor Create(APoint: TPoint); reintroduce; overload; constructor Create(AX, AY: Integer); reintroduce; overload; procedure Assign(Source: TPersistent); override; published property X: Integer read FX write SetX; property Y: Integer read FY write SetY; end; TKante = class(TBase) private FPoints: array[0..1] of TPoint; function GetPoint(Index: Integer): TPoint; procedure SetPoint(Index: Integer; Value: TPoint); protected procedure ReadData(Stream: TStream); override; procedure WriteData(Stream: TStream); override; public constructor Create(AKante: TKante); reintroduce; overload; constructor Create(AStart, AStop: TPoint); reintroduce; overload; constructor Create(AStartX, AStartY, AStopX, AStopY: Integer); reintroduce; overload; destructor Destroy; override; procedure Assign(Source: TPersistent); override; published property Start: TPoint index 0 read GetPoint write SetPoint; property Stop: TPoint index 1 read GetPoint write SetPoint; end; procedure RegisterBaseClasses(const AClasses: array of TBaseClass); function GetBaseClass(const AClassName: String): TBaseClass; procedure SaveList(List: TList; Stream: TStream); // besser ist es eine eigene TList abzuleiten die nur TBaseClass enthalten kann procedure LoadList(List: TList; Stream: TStream); implementation var FClassList: TList = nil; procedure RegisterBaseClasses(const AClasses: array of TBaseClass); var I: Integer; begin Assert(FClassList <> nil); for I := Low(AClasses) to High(AClasses) do if not AClasses[I].InheritsFrom(TBase) then raise Exception.Create('ungültige Klasse in RegisterBaseClasses') else if GetBaseClass(AClasses[I].ClassName) <> nil then raise Exception.CreateFmt('eine Klasse mit Namen "%s" ist schon registriert', [AClasses[I].ClassName]) else FClassList.Add(AClasses[I]); end; function GetBaseClass(const AClassName: String): TBaseClass; var I: Integer; begin Assert(FClassList <> nil); Result := nil; for I := 0 to FClassList.Count -1 do if AnsiCompareText(AClassName, TClass(FClassList[I]).ClassName) = 0 then begin Result := FClassList[I]; Break; end; end; procedure SaveList(List: TList; Stream: TStream); var I: Integer; begin for I := 0 to List.Count -1 do TBase(List[I]).SaveToStream(Stream); end; procedure LoadList(List: TList; Stream: TStream); begin while Stream.Position < Stream.Size do List.Add(TBase.LoadFromStream(Stream)); end; // .TBase procedure TBase.Changed; begin // hier zb. ein NotifyEvent einbauen end; class function TBase.LoadFromStream(Stream: TStream): TBase; var NewClass: TBaseClass; NewName: ShortString; begin Stream.Read(NewName[0], 1); Stream.Read(NewName[1], Ord(NewName[0])); NewClass := GetBaseClass(NewName); if NewClass = nil then raise Exception.CreateFmt('Klasse "%s" ist nicht registriert', [NewName]); Result := NewClass.Create; Result.ReadData(Stream); end; procedure TBase.SaveToStream(Stream: TStream); var NewName: ShortString; begin NewName := ClassName; Stream.Write(NewName[0], Ord(NewName[0]) +1); WriteData(Stream); end; // .TPoint procedure TPoint.SetX(Value: Integer); begin if Value <> FX then begin FX := Value; Changed; end; end; procedure TPoint.SetY(Value: Integer); begin if Value <> FY then begin FY := Value; Changed; end; end; procedure TPoint.ReadData(Stream: TStream); begin Stream.Read(FX, SizeOf(FX)); Stream.Read(FY, SizeOf(FY)); end; procedure TPoint.WriteData(Stream: TStream); begin Stream.Write(FX, SizeOf(FX)); Stream.Write(FY, SizeOf(FY)); end; constructor TPoint.Create(APoint: TPoint); begin inherited Create; Assign(APoint); end; constructor TPoint.Create(AX, AY: Integer); begin inherited Create; X := AX; Y := AY; end; procedure TPoint.Assign(Source: TPersistent); var S: TPoint absolute Source; begin if Source is TPoint then begin if (FX <> S.FX) or (FY <> S.Y) then begin FX := S.FX; FY := S.FY; Changed; end; end else if Source = nil then // bedeutet TPoint(nil) == TPoint(0,0) und ist eine Definitionssache des Programmierers begin if FX or FY <> 0 then // effizienter! als if (FX <> 0) or (FY <> 0) then begin FX := 0; FY := 0; Changed; end; end else inherited Assign(Source); end; // .TKante function TKante.GetPoint(Index: Integer): TPoint; begin if FPoints[Index] = nil then FPoints[Index] := TPoint.Create; // Auto-Allokation beim Zugriff auf Start oder Stop Result := FPoints[Index]; end; procedure TKante.SetPoint(Index: Integer; Value: TPoint); begin GetPoint(Index).Assign(Value); // WICHTIG! niemals ein Object setzen sondern immer dessen EIgenschaften kopieren end; procedure TKante.ReadData(Stream: TStream); begin FreeAndNil(FPoints[0]); FreeAndNil(FPoints[1]); FPoints[0] := TPoint.LoadFromStream(Stream) as TPoint; FPoints[1] := TPoint.LoadFromStream(Stream) as TPoint; end; procedure TKante.WriteData(Stream: TStream); begin Start.SaveToStream(Stream); Stop.SaveToStream(Stream); end; procedure TKante.Assign(Source: TPersistent); begin if Source is TKante then begin Start.Assign(TKante(Source).Start); Stop.Assign(TKante(Source).Stop); Changed; end else if Source = nil then begin FreeAndNil(FPoints[0]); FreeAndNil(FPoints[1]); Changed; end else inherited Assign(Source); end; constructor TKante.Create(AStart,AStop: TPoint); begin inherited Create; Start := AStart; Stop := AStop; end; constructor TKante.Create(AStartX,AStartY,AStopX,AStopY: Integer); begin inherited Create; Start.X := AStartX; Start.Y := AStartY; Stop.X := AStopX; Stop.Y := AStopY; end; constructor TKante.Create(AKante: TKante); begin inherited Create; Assign(AKante); end; destructor TKante.Destroy; begin FreeAndNil(FPoints[0]); FreeAndNil(FPoints[1]); inherited Destroy; end; initialization FClassList := TList.Create; RegisterBaseClasses([TPoint, TKante]); finalization FreeAndNil(FClassList); end. Und ein Testcode
Delphi-Quellcode:
So, das macht aber mindestens 3 Biere, wenn ich den Stoff auf trinken würde ;)
procedure TForm1.Button1Click(Sender: TObject);
var List: TList; Stream: TMemoryStream; I: Integer; begin Stream := TMemoryStream.Create; List := TList.Create; try for I := 0 to 9 do List.Add(TPoint.Create(I, I)); for I := 0 to 9 do List.Add(TKante.Create(I, I, I, I)); SaveList(List, Stream); Stream.SaveToFile('c:\test.bin'); for I := 0 to List.Count -1 do TBase(List[I]).Free; List.Clear; Stream.Position := 0; LoadList(List, Stream); finally List.Free; Stream.Free; end; end; Gruß Hagen |
Re: Referzen in ein Stream speichern
Hallo Hagen
Vielen Dank für das Nachbesseren und das Beispiel. Die drei Biere zahle ich gerne, falls du mal in der Gegend von mir (50. KM südlich) von Lindau am Bodensee bist:)! Ich habe den Code nun eingefügt und laufen gelassen, erhalte aber bei Aufruf von Loadlist leider die Meldung: Klasse '' nicht registriert... Ausserdem fällt mir auf, dass du mit
Delphi-Quellcode:
hier unabhänkgige Objekte anlegst. Meine möchte aber gernefor I := 0 to 9 do List.Add(TPoint.Create(I, I)); for I := 0 to 9 do List.Add(TKante.Create(I, I, I, I)); 10 Punkte anlgen und diese dann über Kanten miteinander verlinken, damit man ein Netz abbilden kann (z.B. eine Landkarte). Deshalb habe ich auch for i:=0 To 4 DO List.add(TKante.Create(List[Random(9)],List[Random(9)]); geschrieben. Kante hält dann zwei Referenzen auf Objekte. Deshalb auch die Problematik beim Speichern, weil die Punkte einer Kante ja nur Referenzen auf Objekte darstellen. ..oder habe ich hier etwas vollkommen falsch verstanden oder mich unklar ausgedrückt? Beste Grüsse und ich werde die Biere auch nicht vergessen:) Geri |
Re: Referzen in ein Stream speichern
Shit, das ist aber konzeptionell eine ganz andere Sache als das was durch deine Postings rübergekommen ist.
Tja dann musst du meinen Source wieder abändern denn durch .Assign() usw. wird in meinen TKante sichergestellt das es IMMER eigene Point Objecte enthält. Konzeptionell ist das ein sehr sicherer Weg um logische Fehler die bei verlinkten Objekten entstehen könne (Freigabe, gemeinsamme Verlinkungen etc.pp) zu vermeiden. Der Fehler "Klasse nicht registrert" kann nur in .LoadFromStream() auftreten weil zb. TPoint nicht mit RegisterBaseClasses() registriert wurde. Wenn du 1 zu 1 meinen Source als Unit und meinen Testcode benutzt hast dann darf KEIN Fehler mehr auftreten. Falls doch ist irgendwo der Wurm drinnen. Welche Delphi Version und welche Compilereinstellungen benutzt du ? Ist schon komisch weil der Source bei mir nach 3 Minuten und wenigen Änderungen (3 Tippfehler + 1 in RegisterBaseClasses()) ssfort lief. Schau mal ob du dich durchbeissen kannst, denn nur wenn man es wirklich selber vom Hirn über die Nerven zu den Fingern und dann als Source in den Computer gehackt hat wird man es wirklich lernen. Gruß Hagen |
Re: Referzen in ein Stream speichern
Hallo negaH
Vielen Dank für Deine Rückmeldung. Ich gebe jetzt vorerste mal auf, weil ich nicht mal weiss, wie ich ansetzen soll und suche mal im Internet nach einem Beispiel. Das ganze entstand auch nur alleine aus Interesse an diesem Problem. Hagen, jedenfalls vielen Dank für all die Hilfe und falls ich positive Ergebnisse aufzeigen kann, dann werde ich sie hier für andere Posten oder wieder einmal ein Tutorial verfassen:). Geri |
Alle Zeitangaben in WEZ +1. Es ist jetzt 08:46 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