Hi,
ich schätze mal das dir noch par Informationen fehlen wie Delphis Klassenkonzept funktioniert.
Dein ClassType ist ein Zeiger in das CodeSegment eines Modules, also EXE,
DLL oder
Package. Der Zeiger zeigt dabei auf die Klassenstruktur. Das ist ein komplexer Record der durch den Compiler anglegt wird, pro deklarierter Klasse, und er enthält die
VMT, DMT, ClassName,
RTTI usw. zu einer Klasse. Wenn du nun deine EXE,
DLL,
Package neu kompilierst dann erzeugt der Compiler jedesmal diese Klassenrecords neu und linkt sie mit allem Code, Daten usw. in die EXE,
DLL,
Package ein. Dabei verschieben siech die Addressen der Zeiger. Also wenn deine Klasse TLine.ClassType = $0040678a sein sollte ist es sehr wahrschenlich das bei Neukompilation dieser Zeiger auf $0040bbbb oder sonstwohin zeigen kann. Dh. der ClassType Zeiger ist absolut ungeeignet als eineindeutige und dauerhaft gültige Referenez für eine Klasse. Aber der ClassName ist relativ unveränderlich. Deshalb schreibt das
VCL Streaming Sytem, das was
DFM's speichert und lädt, statt eienr ID oder des ClassType Pointers einfach jedesmal den Klassennamen des Objektes in den Stream. Speichert man also 1000x TLine Objekte in einen
VCL Stream so steht in dem Stream auch 1000x 'TLine" als String drinnen. Genau genommen #5 + 'TLine', die #5 ist die Längenangabe des Strings. Ist TLine ein leeres Objekt so würde der Stream also schon 7000 Bytes groß sein.
Nun, beim Laden passiert folgendes. Das
VCL System liest den Klassennamen des Objektes das erzeugt werden soll. Hier also 'TLine'. Dann scheut es in einer Liste die aus ClassType Records besteht diesen Klassennamen nach. Man kann mit ClassType.ClassName auf den Klassennamen einer Klasse zugreifen -> siehe Klassenmethoden. Ergo geht Delphi diese Liste er registrierten Klassen durch (siehe GetClass() und RegisterClass()), und vergleich ClassType.ClassName mit Objekt.Klassenname_aus_Stream. Sobald einee Übereinstimmung gefunden wurde gibt die Funktion GetClass() diesen ClassType zurück, deklariert als TClass -> TClass ist ein Zeiger auf eine Klassenhierarchie und damit defakto ein Zeiger auf einen ClassType Record der die Root einer Klassenhierarchie bilden soll.
Damit das funktioniert muß man also einmalig alle Klassen beim System registrieren die man später aus einem Stream laden können möchte.
Statt nun wie die
VCL einfach zu jedem gespeicherten Object immer den vollständigen Klassennamen zu speichern, präferiere ich also eine Stream-globale Index-Tabelle, ein Array das alle im Stream befindlichen Klassen, also deren Namen + ClassType, enthält und deren Indexe in dieses Array statt dem Namen real gespeichert werden. Das hat den Vorteil das der Stream kompakter wird. Beim Laden wird man zuerst dieses Array laden, dann alle ClassType Zeiger aktualisieren auf die real benutzen Zeiger in dem aktuellen Modul. Die gespeicherten ClassType Zeiger können ja absolut falsch sein und an eine beliebige Stelle im Codesegment zeigen. Man muß sie also so korregieren das sie an die Stelle im Codesegment zeigen an der sich auch die Klassendeklaration befindet mit dem gleichen Klassennamen. Dazu nimmt man also den Klassennamen aus dem Array[] und sucht die dazugehörige Klassenstruktur im CodeSegment. Es gibt 2 Wege das zu machen. 1. man geht wie die
VCL vor und benutzt eine globale Liste von registrierten Klassen. Der Entwickler muß dann für alle Klassen RegisterClass() aufrufen damit das Streamingsystem aus einem String als Klassennamen ein TClass und damit ClassType Zeiger machen zu können. Das wäre der geplante und nachvollziehbar richtigere Weg, allerdings mit Arbeit verbunden. Der 2. Weg ist ein Trick von mir. Mit diesem Trick kann man über alle
RTTI -> Run Time Type Information alle zum Prozess gehörigen Module itertieren. Dh. der Trick ermöglicht quasi eine Iteration über alle im Projekt compilierten und deklarierten Typen, zb. zu den Typen Integer, Byte, Char, TColor, TPanel, Enums, Mengen, Interfaces, Klassen usw. usw. werden
RTTI Records im Modul = CodeSegement gespeichert. Man kann nun mit meinem Trick also auch alle im Projekt einglinkten Klassen iterieren und somit zu einem String als Klassennamen die Klasse = TClass = ClassType herausbekomen.
Und exakt das machen wir mit dem geladenen Array of ClassName + ClassType. Zu jedem dort gespeicherten ClassName ermitteln wir den korrekten ClassType Pointer. Wir suchen also pro Klassennamen nur EINMALIG die Class bzw. ClassType. Wennn wir 1000 'TLine' Objekte gespeichert haben so steht 1000x im Stream ein Word, Index in das Array, und am Ende steht ein Array das 'TLine' und einen 4 Bytes Zeiger enthält. Wenn wir Laden holen wir erstmal dieses Array, suchen vom String 'TLine' die korrekte Class: TClass -> ClassType, entweder aus einer eigenen Registrationsliste oder eben mit meinem Trick. Danach laden wir die Objekte. Per Index greifen wir auf's Array zu und holen von dort die korrekte TClass = ClassType. Wir suchen also pro Klasse im Stream nur einmalig die Klasse. Die
VCL sucht dagegen JEDESMAL pro Objekt die Klasse.
Nun, dieses Verfahren hat im Grunde nur Vorteile. Es ist schneller, kompakter als die
VCL. Gegenüber deinem ID basierten System musst du dich hier ich um die Eindeutigkeit der IDs kümmern, das machen wir über den Klassennamen usw. Zudem benötigt dieses System keinen aktiven Code in deinen Klassen, so wie dein ID System. Dein ID System muss nämlich explizit und manuell und permanent sauber durch den Programmierer gepflegt werden, und verursacht wenn es Fehler enthält massive Probleme bei der Wartung der Software. Man kann sich nämlich dumm&dusselig suchen wenn man nicht bemerkt das das ID-System auf Grund von mehrdeutigen IDs ganz andere Klassen geladen hat als das es sich laden sollte.
Unit mit Helperfunctions um alle
RTTI->Run Time Type Infos eines oder aller Module zu einem Prozess zu iterieren:
http://www.delphipraxis.net/internal...065&highlight=
Gruß Hagen
Gruß Hagen