Question sur TStream, TPersistent

Andalarius Messages postés 2 Date d'inscription dimanche 22 mai 2005 Statut Membre Dernière intervention 29 juillet 2005 - 29 juil. 2005 à 16:23
Andalarius Messages postés 2 Date d'inscription dimanche 22 mai 2005 Statut Membre Dernière intervention 29 juillet 2005 - 29 juil. 2005 à 21:49
Bonjour à tous,

Voici ma situation : Je désire écrire les objets complexes (contenant différents objets eux-mêmes ou des listes d'objets) dans un fichier binaire.

Comme plusieurs d'entre-vous le savez déjà, il existe de plusieurs façons d'écrire dans un fichier binaire. Delphi étant très axé "components writing" (tel que dans un .DFM) et que mes objets ne sont pas des composants, j'ai cherché sur google, les newsgroup, les forums (incluant celui-ci) et l'aide de Delphi pour un moyen détourné.

Dans l'aide de Delphi, j'ai trouvé un exemple qui utilise les méthodes :
WriteComponentResFile
et
ReadComponentResFile

L'exemple crée une classe container contenant l'objet dans une property publié (published - seuls les property published sont streamer).

Puis l'exemple crée une autre classe appelé : TStreamableClass qui va publié le conteneur. Ces classes sont enregistrés ( RegisterClasses() ) afin que le RTTI (Run Time Type Information) puisse les reconnaître et les traitées.

L'exemple fonctionne très bien et les attributs membres de type integer et string sont écrits et lus. Cependant, si j'ajoute un objet dans la classe TContainer, en prenant soin de placer la property dans la section published et enregistrer la classe et cette classe hérite de TPersistent (pour qu'elle soit streamer) et les property de cette classe dans la section published.... l'objet n'est pas lu. Je ne sais pas si l'objet est écrit ou non.

Voici l'exemple de l'aide de Delphi que j'ai modifié (3 units):


{Dans la form. ajouté un bouton et ajouté dans l'event OnClick la procédure ci-dessous}

unit ProjetStreamExemple;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, StrmExmpl;

type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Déclarations privées }
public
{ Déclarations publiques }
end;

var
Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
AClassInstance: TStreamableClass;
begin
AClassInstance := TStreamableClass.Create(nil);
WriteComponentResFile('C:\Test', AClassInstance);
FreeAndNil(AClassInstance);

AClassInstance := ReadComponentResFile('C:\Test', nil) as TStreamableClass;
FreeAndNil(AClassInstance);
end;

end.

//------------------------------------
unit StrmExmpl;
{$R-,T-,H+,X+}

interface

uses Classes, Test;

type

TContainedClass = class(TPersistent)
private
FSomeData: Integer;
FSomeTest : TTest;
SSomeString : string;
procedure SetSomeData(Value: Integer);
procedure SetSomeTest(Value: Ttest);
function LireSomeTest: Ttest;
procedure SetSomeString(const Value: string);

public

constructor Create;

// Seules les propriétés publiées
// sont mises automatiquement dans le flux.
published

property SomeData: Integer read FSomeData write SetSomeData;
property SomeTest: Ttest read LireSomeTest write SetSomeTest;
property SomeString : string read SSomeString write SetSomeString;
end;


TStreamableClass = class(TComponent)
private
FContainedClass: TContainedClass;
public
constructor Create(AOwner: TComponent); override;


destructor Destroy; override;

// Seules les propriétés publiées
// sont mises automatiquement dans le flux.
published

property ContainedClass: TContainedClass read FContainedClass write FContainedClass;

end;

implementation

procedure TContainedClass.SetSomeData(Value: Integer);
begin
{ Placer un point d'arrêt ici et remarquer comment les données reviennent dans le flux. }
FSomeData := Value;
end;

procedure TContainedClass.SetSomeTest(Value: Ttest);
begin
FSomeTest := Value;
end;

constructor TContainedClass.Create;
begin
FSomeData := 42;
SSomeString := 'Adam';
FSomeTest := TTest.create;
end;

constructor TStreamableClass.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FContainedClass := TContainedClass.Create;
end;

destructor TStreamableClass.Destroy;
begin
FContainedClass.Free;
end;

function TContainedClass.LireSomeTest: Ttest;
begin
Result := FSomeTest;
end;

procedure TContainedClass.SetSomeString(const Value: string);
begin
SSomeString := Value;
end;

initialization
RegisterClasses([TContainedClass, TTest, TStreamableClass]);
finalization

end.

//-------------------------------------------------------------------------------
unit Test;

interface

uses Classes;

type
Ttest = class(TPersistent)
private
fValue : string;
sValue : integer;
function ReadValue1: string;
function ReadValue2: integer;
procedure WriteValue1(const Value: string);
procedure WriteValue2(const Value: integer);
public
constructor Create;
published
property Value1 : string read ReadValue1 write WriteValue1;
property Value2 : integer read ReadValue2 write WriteValue2;
end;

implementation

{ Ttest }

constructor Ttest.Create;
begin
inherited create;
fValue := 'Adam';
sValue := 1;
end;

function Ttest.ReadValue1: string;
begin
Result := fValue;
end;

function Ttest.ReadValue2: integer;
begin
Result := sValue;
end;

procedure Ttest.WriteValue1(const Value: string);
begin
fValue := Value;
end;

procedure Ttest.WriteValue2(const Value: integer);
begin
sValue := Value;
end;

end.




Ma question est la suivante : est-ce qu'il est possible d'écrire et de lire l'objet de la classe TTest selon cette méthode ?

Je ne peux pas utilisé les TCollections et TCollectionItem car dans mon programme principal (pas cet exemple), les objets contiennent une liste d'objets différents.

J'aimerais pouvoir éviter d'écrire chaque objet individuellement en créant des nouvelles fonctions de streaming, spécifiant la taille des string, integer et autre avant.... à cause de la complexité du programme principal.

Merci à l'avance,

Andalarius

2 réponses

yvemoreau Messages postés 308 Date d'inscription mardi 11 juin 2002 Statut Membre Dernière intervention 26 septembre 2008
29 juil. 2005 à 19:10
bon j'ai testé un peu , si la classe existe elle est accessible

procedure TForm1.Button1Click(Sender: TObject);
var
AClassInstance: TStreamableClass;


begin
AClassInstance := TStreamableClass.Create(nil);
try
try
memo1.clear;
if(AClassInstance<>nil)then
if(AClassInstance.ContainedClass<>nil)then
if(AClassInstance.ContainedClass.SomeTest<>nil)then
memo1.Lines.Add((AClassInstance.ContainedClass.SomeTest.Value1));
memo1.Lines.Add((AClassInstance.ContainedClass.SomeString));
memo1.Lines.Add(IntToStr(AClassInstance.ContainedClass.SomeData));
memo1.Lines.Add(IntToStr(AClassInstance.ContainedClass.SomeTest.Value2));

except;
end;


finally
FreeAndNil(AClassInstance);
end;
end;

evidemment les test <>nil peuvent être omis puisque les exceptions sont gérées...

yve
0
Andalarius Messages postés 2 Date d'inscription dimanche 22 mai 2005 Statut Membre Dernière intervention 29 juillet 2005
29 juil. 2005 à 21:49
Ok, je me suis mal expliqué.

Est-ce qu'il est possible de lire la classe test APRÈS que l'objet container est été streamer dans le fichier. Faire la lecture de l'objet après le
ReadComponentResFile

Andalarius
0