AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Code-Bibliothek Library: Object-Pascal / Delphi-Language Delphi [TCustomStore] Klasse, die sich komplett selber speichert
Thema durchsuchen
Ansicht
Themen-Optionen

[TCustomStore] Klasse, die sich komplett selber speichert

Ein Thema von sirius · begonnen am 12. Jul 2007
Antwort Antwort
Benutzerbild von sirius
sirius

Registriert seit: 3. Jan 2007
Ort: Dresden
3.443 Beiträge
 
Delphi 7 Enterprise
 
#1

[TCustomStore] Klasse, die sich komplett selber speichert

  Alt 12. Jul 2007, 23:05
Beschreibung

Jede Klasse, die von TCustomStore (siehe Anhang) abgeleitet wird kann sich komplett selber in einen Stream (oder nonVCL-Unit: in einen String) speichern, und natürlich auch davon wiederherstellen.

Und dabei ist es völlig irrelevant, welche Variablen als Properties definiert wurden. Es werden alle Variablen gespeichert. Diese Varibalen können statisch oder dynamisch sein, also alles was Delphi so an Typen kennt inkl. Records und weiterer Klassen. (Es können aber auch einzelne Variablen davon ausgenommen werden)

Anwendung
Ich stelle einfach kurz ein Beispiel vor:
Delphi-Quellcode:
//Diese Klasse ist von TCustomStore abgeleitet und bringt deswegen die Methoden
//SaveToStream und LoadFromStream mit.

//die nonVCL-Variante muss natürlich ohne Stream auskommen und verwendet daher Strings

type TStore=class(TCustomStore)
      public
       i:integer;
       Store2:TStore_2;
       Strings:TStrings;
       other_class:TMyOwn;
       p:pointer; //der soll in diesem Beispiel nicht gespeichert werden

       constructor create; //Im constructor werde ich hier die Klassen registrieren.
       //Dies kann allerdings auch später passieren.
end;


constructor TStore.create;
begin
  inherited create;
  
  Store2:=TStore2.Create;
  
  Strings:=tstringlist.Create;

  other_class:=tmyown.Create;


  //der wichtige Teil, der gemacht werden muss, da Klassen nicht in der RTTI enthalten sind
  RegisterObject(@Store3);
  //die Registrerung über RegisterObject geht für Klassen, die
  // -von TCdustomStore abgleitet sind
  // -keine dynamischen Varibalen enthalten
  // -mittels der Ereignisbehandlung ClassSaveEvent und ClassLoadEvent gespeichert werden
  
  //Für andere Klassen müssen die Stream-Methoden mit angegeben werden
  //die Methoden müssen vom Typ "procedure(Stream:TStream) of object" sein
  //Der Stream, der übergeben wird ist exklusiv, mann muss sich also nicht die Länge merken
  RegisterobjectMethod(@other_class,
                       other_class.Save,
                       other_class.Load);
  RegisterObjectMethod(@Strings,
                       Strings.SaveToStream,
                       Strings.LoadFromStream);
  
  //und noch Variablen/Klassen registrieren die nicht gespeichert werden sollen
  //in dem Fall, der untypisierte pointer
  Registerdontsave(@p,sizeof(p));
  //letztenendes bedeutet dies Registrierung, dass der pointer durch
  //LoadFromStream nicht überschrieben wird


  //Klassen innerhalb von Records und dynamischen Arrays können nicht registriert werden
  //und sollten daher nicht verwendet werden --> in eine Klasse kapseln
  //RegisterDontSave ist nur für Pointer und Klassen gedacht
  //--> geht definitiv nicht für Records und Dynamische Arrays
end;

//speichern der Klasse TStore
procedure TForm1.SaveButtonClick(Sender: TObject);
var Class_to_Store:TStore;
    File:TFileStream;
begin
  Class_to_Store:=TStore.create;
  
  Class_to_Store.i:=9;
  Class_to_Store.Store2.x:='Hallo'; //eine dynamische String-Variable aus TStore3 belegen
  Strings.add('Lala');
  other_class.s:='Hilfe';
  Class_to_Store.p:=pointer(10);


  File:=TfileStream.Create('Test_SPclass.dat',fmcreate);
  Class_to_Store.SaveToStream(File);
  File.free;

  Class_to_Store.free;
  //Da Class_to_Store.FreeAllObjects per default auf True ist, werden alle registrierten
  //Klassen von RegisterObject und RegisterObjectMethods mit freigegeben.
  //Andere Klassen und Variablen (auch die von RegisterDontSave) müssen selber freigegeben werden
end;

//Laden der Klasse TStore
procedure TForm1.LoadButtonClick(Sender: TObject);
var Class_Stored:TStore;
    File:TFilestream;
begin

  memo1.clear;

  Class_Stored:=TStore.create;
  Class_Stored.i:=-1; //zum Test, dass sich nach loadfromstream auch etwas ändert
  Class_Stored.x:='Das will ich gleich nicht mehr sehen';
  Class_Stored.p:=pointer(50); //Der sollte gleich bleiben

  File:=TfileStream.Create('Test_SPclass.dat',fmopenread);
  Class_Stored.LoadfromStream(File);
  File.free;

  memo1.lines.add(Class_Stored.Store2.x);
  memo1.lines.Add(inttostr(Class_Stored.i));
  memo1.lines.AddStrings(Class_Stored.Strings);
  memo1.lines.Add(Class_Stored.other_class.s);
  memo1.lines.add(inttostr(integer(Class_Stored.p)));

  Class_Stored.free;
end;
Delphi-Quellcode:

//hie noch die Testklassen, die in der eigentlichen Klasse verwendet werden
type tmyown=class
        s:string;
        procedure save(Stream:Tstream);
        procedure Load(Stream:Tstream);
end;
type TStore2=class(TCustomStore)
      public
       x:string;
end;


//Methoden der HilfsTestKlassen
procedure TmyOwn.save;
begin
  stream.Write(s[1],length(x));
end;
procedure Tmyown.Load;
begin
  setlength(s,stream.size);
  stream.read(s[1],length(s));
end;

Soweit dazu.


Ansonsten noch der Text, der auch in der Unit steht:
Zitat:
unit U_Store_Classes
benötigt U_Store_Dynamic_Params

Inhalt
======
class "TCustomStore"

Beschreibung
============
Klassen, die von TcustomStore abgeleitet werden bringen folgende zwei Methoden mit
-SaveToStream
-LoadFromStream
Damit wird der komplette Inhalt (Records, strings, dynamische arrays, ...)
der Klasse im Stream gespeicher bzw. aus dem Stream geladen. Dies geschieht
unabhängig von den definierten Properties.
Zusätzlich werden alle registrierten Klassen die in der Basisklasse
enthalten sind, gespeichert/geladen.

Je nach Einstellung von der Eigenschaft FreeallObjects werden alle
registrierten Objekte freigegeben

Verwendung
==========
type TTest=class(TCustomStore)
private
x:integer;
t:string;
n:array of string;
Int_Store:TStore; //abgeleitet von TCustomStore

myclass:Tmyclass;

p:pointer;
timer:Ttimer;
public
//... hier können auch weiter Variablen stehen
constructor create; override;
end;

//....
constructor TTest.create;
begin
list:=Tstringlist.create;
myclass:=tmyclass.create;

//ganz wichtig, jede Klasse muss vor der Verwendung von
//savetostream/loadfromstream registriert werden
RegisterObject(@int_Store);
//dies gilt nur für bestimmte Klassen (siehe unten)

//alternativ können auch Klasse (und auch Variablen, wie pointer)
//als Don't-Save registriert werden.
//Dadurch wird beim Laden, der ursprüngliche Inhalt in der Klasse nicht überschrieben
RegisterdontSave(@p,sizeof(p));
Registerdontsave(@timer) //size=4 ist standard für Klassen
//Hierbei ist die Größe der Variablen anzugeben


//Klassen, die nicht wie oben registriert werden können
//und ihre eigenen SaveToStream und LoadFromStream -methoden mitbringen
//können wie folgt registriert werden
RegisterObjectMethod(@myclass,myclass.savetostream ,myclass.loadfromstream);


end;

//
Aufruf:
var test:Ttest;
stream:TFilestream;
begin
test:=TTest.create;

//...Eigenschaften setzen etc.

//Zustand speichern
stream:=Tfilestream.create('xyz.dat',fmcreate);
test.savetofile(stream);
stream.free;

//...

//Zustand wieder laden
stream:=Tfilestream.create('xyz.dat',fmopenread);
test.Loadfromfile(stream);
stream.free;

//...

test.free; //Die StringList wird auch freigegeben

end;


Für Klassen innerhalb der Basisklasse, die nicht von TCustomStore abgeleitet
werden, kann auch das Ereignis TClassStoreEvent (ClassLoadEvent und ClassSaveEvent)
definiert werden. Dieses wird ausgelöst, sobald eine Klasse von RegisterObject
gespeichert oder geladen werden soll, die nicht TCustomStore als Vorfahr hat.
Diese Ereignisbehandlungsroutine kann dann das Speichern des Objektes übernehmen.

Desweiteren können alle Klassen, die in ihren Variablen keine
weiteren Klassen enthalten direkt registriert (RegisterObject) werden.


Hinweise
========
1. Klassen müssen mit RTTI compiliert werden (siehe Compilerdirektive $M+)
2. Es können keine Interfaces bearbeitet werden
3. NICHTREGISTRIERTE Klassen werden beim laden wahrscheinlich ungültig
==>Speicherlöcher
4. Eine untergeordnete Klasse, die beim Aufruf von SavetoStream instanziert,
war, muss es auch noch/wieder bei LoadfromStream sein.
5. Es wird nicht kontrolliert, ob Variablen/Klassen mehrfach in verschiedenen
Listen registriert wurden (sollte vermieden werden, da es zu unerwünschten
Effekten kommen kann)
6. Es können keine Klassen innerhalb von dynamischen Komponenten (
Records und dynamischen Arrays) registriert werden
7. RegisterDontSave geht nur für einfache Pointer oder für Klassen
Es geht nicht für Records und Arrays.
Wichtig!! Es wird die Unit aus diesem Thread benötigt.
(Ich will die Unit nicht hier nochmal anhängen. Falls ich an ihr noch etwas ändere müsste ich sonst in beiden Threads updaten)


Edit 19.7.07 23:15 -- Überarbeitete Version hochgeladen
Edit 20.7.07 10:00 -- Noch ein Beispielprojekt angefertigt
Edit 08.8.07 21:15 -- nonVCL-Variante ergänzt
Angehängte Dateien
Dateityp: pas u_store_classes_133.pas (13,4 KB, 76x aufgerufen)
Dateityp: zip classrtti_142.zip (13,8 KB, 55x aufgerufen)
Dateityp: pas u_store_classes_nonvcl_133.pas (12,9 KB, 51x aufgerufen)
Dieser Beitrag ist für Jugendliche unter 18 Jahren nicht geeignet.
  Mit Zitat antworten Zitat
Antwort Antwort

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 09:00 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz