IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Free Pascal Discussion :

Sauvegarder/Charger automatiquement les données d'un objet [Free Pascal]


Sujet :

Free Pascal

  1. #1
    Membre régulier Avatar de TheFreeBerga
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    63
    Détails du profil
    Informations personnelles :
    Âge : 55
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 63
    Points : 77
    Points
    77
    Par défaut Sauvegarder/Charger automatiquement les données d'un objet
    Bonjour @ toutes & à tous,

    Non pas que je crois que c'est déjà noël mais je sais qu'il existe des pans de la programmation objet qui m'échappent encore alors, benoîtement, je pose la question:
    Est-il possible de sauvegarder les données d'un objet puis de les recharger dans un objet de même classe sans écrire le code qui va avec ?
    Je me doute bien que cela n'est pas possible naturellement mais moyennent une petite "préparation" de l'objet concerné, est-ce seulement envisageable ?

    Merci d'avance pour vos lumières !

  2. #2
    Rédacteur/Modérateur

    Avatar de Roland Chastain
    Homme Profil pro
    Enseignant
    Inscrit en
    Décembre 2011
    Messages
    4 072
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Enseignant

    Informations forums :
    Inscription : Décembre 2011
    Messages : 4 072
    Points : 15 462
    Points
    15 462
    Billets dans le blog
    9
    Par défaut
    Bonjour !

    Personnellement je ne suis même pas sûr de bien comprendre la question, cela dit sans aucune ironie. C'est pour quoi faire ?

    P.-S. En fait, ça me rappelle des exemples de code que j'avais rassemblés quand j'étudiais l'unité Objects. Par exemple ça :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
     
    program objects_pmemorystream_02;
     
    {$IFDEF VPASCAL}{$PMTYPE VIO}{$ELSE}{$APPTYPE CONSOLE}{$ENDIF}
     
    uses
    {$IFDEF VPASCAL}
      Strings,
    {$ENDIF}
      Objects, Crt;
     
    var
      p: pChar;
      strm: pStream;
     
    begin
      TextBackground(Blue);
     
      p := 'samedi 10 mai 2014';
     
      WriteLn(p);
     
      strm := New(pMemoryStream, Init(100, 10));
      strm^.StrWrite(p);
      strm^.Seek(0);
     
      p := nil;
      p := strm^.StrRead;
     
      Dispose(strm, Done);
     
      WriteLn(p);
     
      FreeMem(p, StrLen(p) + 1);
     
      TextBackground(Black);
      Write('Appuyez sur la touche Entr'#130'e... ');
      ReadLn;
    end.
    Est-ce que cet exemple se rapproche de ce à quoi tu penses ?
    Mon site personnel consacré à MSEide+MSEgui : msegui.net

  3. #3
    Membre régulier Avatar de TheFreeBerga
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    63
    Détails du profil
    Informations personnelles :
    Âge : 55
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 63
    Points : 77
    Points
    77
    Par défaut
    Oui et non.
    Je vais essayer d'être plus clair dans ma question :
    En cours de développement d'une application client/serveur, j'ai déjà fabriqué tout ce qu'il me faut pour les communications : un flux compressé et chiffré, un objet script qui me permet de facilement programmer une conversation entre les deux extrémités, tout ça dans l'esprit qu'étant seul à développer ce projet, j'ai besoin d'outils qui me facilitent la tâche, tant au niveau de la rapidité du développement que pour minimiser les risques d'erreurs.
    Par exemple, l'objet script me permettant de définir les différentes actions à réaliser pour une transaction particulière peut être indifféremment défini en premier côté serveur ou client puisqu'il me suffit par la suite de faire un copier/coller du code et d'inverser les ordres.
    Tout ça toujours dans un esprit de rapidité de développement et pour réduire les erreurs bêtes (oubli d'une commande dans le cheminement, erreur dans une constante, etc... ).
    Du coup, dans le même esprit, il me serait bien pratique de posséder un objet dont les propriétés seraient "automatiquement" écrites ou lues au travers d'une méthode.
    Ainsi, je n'aurai qu'à déclarer un objet pour chaque type de données à échanger entre le serveur et le client, initialiser les données au départ, écrire cet objet dans mon flux et le récupérer de l'autre côté dans un objet de même type.
    Le but final étant, tu l'auras bien compris, de ne pas écrire toutes méthodes read/write de chaque données à échanger.
    A force d'y réfléchir, n'est-ce pas ce que font les composants que l'on peut déposer sur une fiche ou les concepteurs doivent se palucher une à une chaque propriété et leur couple de read/write ?

  4. #4
    Rédacteur/Modérateur
    Avatar de M.Dlb
    Inscrit en
    Avril 2002
    Messages
    2 464
    Détails du profil
    Informations personnelles :
    Âge : 39

    Informations forums :
    Inscription : Avril 2002
    Messages : 2 464
    Points : 4 311
    Points
    4 311
    Par défaut
    Comment as-tu implémenté ton "flux" ? Une chaîne de caractères ?
    Ces flux ne sont qu'une suite d'octets, qui peuvent représenter des objets dont les champs sont déjà renseignés. Transformer des objets en flux (en faire l'opération inverse) s'appelle la sérialisation ("serialization" en anglais). Tu devrais trouver des exemples à travers Google.

    En Delphi, donc en Free Pascal, ca devrait être pareil, on utilise aussi beaucoup les objets TStream (et descendants) qui contiennent déjà des primitives pour lire/écrire des objets dans un flux.
    M.Dlb - Modérateur z/OS - Rédacteur et Modérateur Pascal

  5. #5
    Membre régulier Avatar de TheFreeBerga
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    63
    Détails du profil
    Informations personnelles :
    Âge : 55
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 63
    Points : 77
    Points
    77
    Par défaut
    Merci pour toutes ces informations, ça ressemble bien à ce que je recherche.

    Effectivement, j'utilise un descendant de TMemoryStream auquel j'ai ajouté des méthodes Encode/Decode[DWord/AnsiString] que j'utilise pour le chiffrement alors que les méthodes standards read/write sont utilisées par Synapse lorsque je lui passe mon objet en paramètre pour réaliser la transmission du flux chiffré.

    En Delphi, donc en Free Pascal, ca devrait être pareil, on utilise aussi beaucoup les objets TStream (et descendants) qui contiennent déjà des primitives pour lire/écrire des objets dans un flux.
    A ce sujet, un petit exemple d'un objet qui sait écrire ses données dans un flux alors que le code correspondant n'a pas été explicitement écrit ?

    En attendant, je me mets à la recherche de cette fameuse sérialisation.

  6. #6
    Rédacteur/Modérateur
    Avatar de M.Dlb
    Inscrit en
    Avril 2002
    Messages
    2 464
    Détails du profil
    Informations personnelles :
    Âge : 39

    Informations forums :
    Inscription : Avril 2002
    Messages : 2 464
    Points : 4 311
    Points
    4 311
    Par défaut
    Tu peux juste écrire deux procédures/fonctions pour ton objet, on les appelle généralement SaveToStream et ReadFromStream, en passant le Stream en paramètre. Après il faut jouer avec les primitives de bases que sont Read et Write.

    Pas d'exemple dans mes cartons, désolé
    M.Dlb - Modérateur z/OS - Rédacteur et Modérateur Pascal

  7. #7
    Membre régulier Avatar de TheFreeBerga
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    63
    Détails du profil
    Informations personnelles :
    Âge : 55
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 63
    Points : 77
    Points
    77
    Par défaut
    Oui, je vois bien ce dont tu parles et c'est justement ce que je veux éviter ! Écrire le code de lecture/écriture de chaque données.

    Mes recherches ont commencées à porter leurs fruits : j'arrive à écrire dans un flux des données pour lesquelles je n'ai pas écrits de code:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
     
    unit Unit1;
     
    {$mode objfpc}{$H+}
     
    interface
     
    uses
      Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;
     
    type
     
      TMyData = Class( TComponent )
        PROTECTED
          _FieldInteger: Integer;
          _FieldString: String;
        PUBLISHED
          property FieldInteger: Integer read _FieldInteger write _FieldInteger;
          property FieldString: String read _FieldString write _FieldString;
    	end;
     
     
    	{ TForm1 }
      TForm1 = class(TForm)
    		Button1: TButton;
    		Button2: TButton;
    		procedure Button1Click(Sender: TObject);
    		procedure Button2Click(Sender: TObject);
     
      private
            { private declarations }
      public
            { public declarations }
      end;
     
     
    var
      Form1: TForm1;
     
     
    implementation
     
    {$R *.lfm}
     
    { TForm1 }
     
    procedure TForm1.Button1Click(Sender: TObject);
    var
      d : TMyData;
      s : TFileStream;
      w : TWriter;
     
    begin
     
      s := TFileStream.Create( './properties.dat', fmCreate );
      w := TWriter.Create( s, 16384 );
     
      d := TMyData.Create(nil);
      d.FieldInteger := 1969;
      d.FieldString := 'Eleonore ma chaine.';
      d.WriteState(w);
      FreeAndNil(d);
     
      FreeAndNil(w);
      FreeAndNil(s);
     
     
    end;
     
     
     
    procedure TForm1.Button2Click(Sender: TObject);
    var
      d : TMyData;
      s : TFileStream;
      r : TReader;
     
    begin
     
      s := TFileStream.Create( './properties.dat', fmOpenReadWrite );
      r := TReader.Create( s, 16384 );
     
      d := TMyData.Create(nil);
      d.ReadState(r);
     
      ShowMessage( Format( '%s , %d', [d.FieldString,d.FieldInteger] ) );
     
      FreeAndNil(d);
      FreeAndNil(r);
      FreeAndNil(s);
     
    end;
     
    end.
    Mais la procédure de lecture plante pour le moment et, autant la string est bien écrite, par contre l'integer (valeur 1969) est codé sur 2 octets encadré de chaque côté par un octet non nul...

  8. #8
    Membre régulier Avatar de TheFreeBerga
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    63
    Détails du profil
    Informations personnelles :
    Âge : 55
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 63
    Points : 77
    Points
    77
    Par défaut Trouvé !
    C'est tout bon !

    J'ai trouvé comment faire. A posteriori, cela fonctionne avec les types simples (integer, currency, real, ..., string ) mais aussi avec un TStringList ! J'imagine que c'est parce qu'il possède des méthodes d'accès aux flux... A tester avec d'autres objets qui savent travailler avec les flux.
    A noter que le TStringList DOIT ÊTRE créé avant le chargement.
    Autre chose, si pour vous un octet est un octet, alors définissez des noms de propriétés (celles publiées) courts, voir d'un seul caractère car leurs noms sont écrits littéralement dans le flux de sortie(*).

    Voilà !

    Merci de votre aide & à bientôt !

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
     
    unit Unit1;
     
    {$mode objfpc}{$H+}
     
    interface
     
    uses
      Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;
     
    type
     
      TMyData = Class( TComponent )
        PRIVATE
          _FieldInteger: Integer;
          _FieldString: String;
          _FieldCurr: Currency;
          _StringList: TStringList;
     
        PUBLISHED
          property FieldInteger: Integer read _FieldInteger write _FieldInteger;
          property FieldString: String read _FieldString write _FieldString;
          property FieldCurr: Currency read _FieldCurr write _FieldCurr;
          property StringList: TStringList read _StringList write _StringList;
     
       end;
     
     
      { TForm1 }
      TForm1 = class(TForm)
    		Button1: TButton;
    		Button2: TButton;
    		procedure Button1Click(Sender: TObject);
    		procedure Button2Click(Sender: TObject);
     
      private
            { private declarations }
      public
            { public declarations }
      end;
     
     
    var
      Form1: TForm1;
     
     
    implementation
     
    {$R *.lfm}
     
    { TForm1 }
    procedure TForm1.Button1Click(Sender: TObject);
    var
      d : TMyData;
      s : TFileStream;
     
    begin
     
      s := TFileStream.Create( './properties.dat', fmCreate );
      d := TMyData.Create(nil);
     
      d.FieldInteger := 1969;
     
      d.FieldString := 'Eleonore ma chaine.';
     
      d.FieldCurr := 123456.789;
     
      d.StringList := TStringList.Create;
      d.StringList.Add( 'Et cette fois ?' );
      d.StringList.Add( 'Quelque chose dans le fichier ?' );
      d.StringList.Add( 'dis moi oui !' );
     
      s.WriteComponent(d);
     
      FreeAndNil(s);
      FreeAndNil(d);
     
    end;
     
     
     
    procedure TForm1.Button2Click(Sender: TObject);
    var
      d : TMyData;
      s : TFileStream;
      c : TMyData;
     
    begin
     
      s := TFileStream.Create( './properties.dat', fmOpenRead );
      d := TMyData.Create(nil);
      d.StringList := TStringList.Create;
      s.ReadComponent(d);
     
      ShowMessage( Format( '| %s | %d | %d | %m |', [d.FieldString,d.FieldInteger,d.StringList.Count,d.FieldCurr] ) + #13 + d.StringList.Text );
     
      Try
        c := TMyData.Create(nil);
        c.StringList := TStringList.Create;
        // CRASH !
        s.ReadComponent(c);
      Except
        s.Position := 0;
        // OK !
        s.ReadComponent(c);
        // La preuve !
        ShowMessage( Format( '| %s | %d | %d | %m |', [c.FieldString,c.FieldInteger,c.StringList.Count,c.FieldCurr] ) + #13 + c.StringList.Text );
      end;
     
      FreeAndNil(s);
      FreeAndNil(d);
      FreeAndNil(c);
     
    end;
     
     
    end.
    (*): Astuce, déclarer les champs de données avec des noms lisibles dans une section PUBLIC, les noms de propriétés sur un seul caractère pour optimiser la taille du flux de sortie et travailler uniquement avec les champs de données publics pour une meilleure lisibilité du code.

  9. #9
    Rédacteur/Modérateur
    Avatar de M.Dlb
    Inscrit en
    Avril 2002
    Messages
    2 464
    Détails du profil
    Informations personnelles :
    Âge : 39

    Informations forums :
    Inscription : Avril 2002
    Messages : 2 464
    Points : 4 311
    Points
    4 311
    Par défaut
    Pourquoi tu utilises un objet TComponent pour tes données ? Un simple record ou un objet TObject devrait suffire pour ce genre de traitement (un échange d'information à travers un protocole de communication). Avec un record ou un objet, on pourra aussi utiliser Read et Write, au lieu de ReadComponent et WriteComponent.
    M.Dlb - Modérateur z/OS - Rédacteur et Modérateur Pascal

  10. #10
    Membre régulier Avatar de TheFreeBerga
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    63
    Détails du profil
    Informations personnelles :
    Âge : 55
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 63
    Points : 77
    Points
    77
    Par défaut
    Parce que c'est bien le but de la manœuvre. Sauvegarder des données dans un flux sans écrire une seule ligne de code pour les opérations d'écritures/lectures.
    L'utilisation de read/write comme tu le préconise implique nécessairement de faire un appel par champ en indiquant sa taille.
    L'utilisation d'un TComponent me permet de sauter cette étape, il n'y a qu'à déclarer ses champs et les propriétés publiées associées et le tour est joué (voir code).
    Concrètement et d'un point de vue purement pragmatique, mon risque d'erreur dans les échanges de données entre le serveur et le client est tombé à zéro puisque cette méthode d'échange de données n'est pas assujettie à l'écriture du code correspondant, mais seulement dans la déclaration d'un objet TComponent et de ses champs; les méthodes WriteComponent et ReadComponent du flux se chargeant à ma place des opérations d'entrés/sorties.
    J'espère avoir été plus clair cette fois ci et encore merci de t'être penché sur mon sujet.

    @+

  11. #11
    Rédacteur/Modérateur
    Avatar de M.Dlb
    Inscrit en
    Avril 2002
    Messages
    2 464
    Détails du profil
    Informations personnelles :
    Âge : 39

    Informations forums :
    Inscription : Avril 2002
    Messages : 2 464
    Points : 4 311
    Points
    4 311
    Par défaut
    J'ai revu ton code, en utilisant un record pour l'échange de données. C'est typiquement ce qui est fait, pour un protocole de communication. On évite d'envoyer trop de données (en essayant d'optimiser !) donc on évite d'envoyer des objets déjà alloués comme des TStringGrid. En revanche, si on est obligés d'envoyer un nombre variable de chaînes de caractères, on a typiquement une procédure qui transforme une StringList en texte avec des séparateurs pour chaque ligne, et on envoie cette chaîne encodée. De l'autre côté du tuyau, on décrypte la chaîne et on alimente un nouveau StringList.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    unit Unit1;
     
    interface
     
    uses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls;
     
    type
      TMyData = record
        FieldInteger: Integer;
        FieldString: String;
        FieldCurr: Currency;
      end;
     
      TForm1 = class(TForm)
        Button1: TButton;
        Button2: TButton;
        procedure Button1Click(Sender: TObject);
        procedure Button2Click(Sender: TObject);
        procedure FormCreate(Sender: TObject);
        procedure FormDestroy(Sender: TObject);
      private
        s : TMemoryStream;
      public
        { Déclarations publiques }
      end;
     
    var
      Form1: TForm1;
     
    implementation
     
    {$R *.dfm}
     
    procedure TForm1.Button1Click(Sender: TObject);
    var
      d : TMyData;
    begin
      d.FieldInteger := 1969;
      d.FieldString := 'Eleonore ma chaine.';
      d.FieldCurr := 123456.789;
      s.Write(d, sizeOf(TMyData));
      ShowMessage(IntToStr(s.Position));
    end;
     
    procedure TForm1.Button2Click(Sender: TObject);
    var
      d : TMyData;
      c : TMyData;
    begin
      s.Position := 0;
      s.Read(d, sizeOf(TMyData));
      ShowMessage( Format( '| %s | %d | %m |', [d.FieldString, d.FieldInteger, d.FieldCurr] ));
      Try
        s.Read(c, sizeOf(TMyData));
      Except
        s.Position := 0;
        s.Read(c, sizeOf(TMyData));
        ShowMessage( Format( '| %s | %d | %m |', [c.FieldString, c.FieldInteger, c.FieldCurr] ));
      end;
    end;
     
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      s := TMemoryStream.Create;
    end;
     
    procedure TForm1.FormDestroy(Sender: TObject);
    begin
      s.Free;
    end;
     
    end.
    M.Dlb - Modérateur z/OS - Rédacteur et Modérateur Pascal

  12. #12
    Membre régulier Avatar de TheFreeBerga
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    63
    Détails du profil
    Informations personnelles :
    Âge : 55
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 63
    Points : 77
    Points
    77
    Par défaut
    Je comprends tout à fait ta démarche et j'abonde en ton sens, je suis plutôt du genre "un octet est un octet" (j'ai commencé sur un ZX-81 avec 1Ko de ram) mais là, ma problématique est de blinder mon développement avec une stratégie d'échange de données qui minimise au mieux les fautes de frappes, oublis en tout genre, etc...
    D'où mon idée d'une structure de données dont au aurait pas besoin d'écrire les méthodes de lecture/écriture pour juguler les problèmes cités plus haut.
    J'ai modifié mon code en ajoutant les statistiques d'inflation de cette méthode. Il s'avère que plus les données sont importantes, plus le taux d'inflation baisse. Je suis tombé à moins de 14% en imaginant transférer les noms des 36826 communes. Plus le taux d'inflation grimpe, plus le nombre d'octets concernés est ridicule.
    Tout ça en sachant que de toute façon, dans mon cas présent, ces données seront passées dans un flux de compression avant d'être physiquement transmises (je passe l'étape du chiffrement qui fait du 1:1 ).

    PS: Afin d'optimiser au mieux la taille du flux de sortie, j'ai appliqué l'addentum ajouté dans un billet précédent en nommant mes propriétés publiées d'un seul caractère.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
     
    unit Unit1;
     
    {$mode objfpc}{$H+}
     
    interface
     
    uses
      Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;
     
    type
     
      TMyData = Class( TComponent )
        PUBLIC
          FieldInteger: Integer;
          FieldString: String;
          FieldCurr: Currency;
          StringList: TStringList;
     
        PUBLISHED
          property A: Integer read FieldInteger write FieldInteger;
          property B: String read FieldString write FieldString;
          property C: Currency read FieldCurr write FieldCurr;
          property D: TStringList read StringList write StringList;
     
    	end;
     
     
    	{ TForm1 }
      TForm1 = class(TForm)
    		Button1: TButton;
    		Button2: TButton;
    		procedure Button1Click(Sender: TObject);
    		procedure Button2Click(Sender: TObject);
     
      private
            { private declarations }
      public
            { public declarations }
      end;
     
     
    var
      Form1: TForm1;
     
     
    implementation
     
    {$R *.lfm}
     
    { TForm1 }
    procedure TForm1.Button1Click(Sender: TObject);
    var
      d : TMyData;
      s : TFileStream;
      i : Integer;
      x : Integer;
      y : Integer;
     
    begin
     
      s := TFileStream.Create( './properties.dat', fmCreate );
      d := TMyData.Create(nil);
     
      d.FieldInteger := 1969;
     
      d.FieldString := 'Eleonore ma chaine.';
     
      d.FieldCurr := 123456.789;
     
      d.StringList := TStringList.Create;
      for i := 1 to 36826 do
        d.StringList.Add( StringOfChar( Chr(64+Random(26)), 1+Random(10) ) );
     
      s.WriteComponent(d);
     
      // Taille des données brutes
      x := 0;
      for i := 0 to d.StringList.Count-1 do
        x := x + Length( d.StringList[i] ) + 1; // +1 = Il faut bien compter un séparateur OU une longueur
      x := x + Length( d.FieldString ) + 1;
      x := x + SizeOf( d.FieldInteger );
      x := x + SizeOf( d.FieldCurr );
     
      FreeAndNil(s);
      FreeAndNil(d);
     
      // Taille du fichier final
      y := FileSize( './properties.dat' );
     
      ShowMessage( Format( 'Inflation: %d octets , %2.2f %%', [y-x,100-((x/y)*100)] ) );
     
    end;
     
     
     
    procedure TForm1.Button2Click(Sender: TObject);
    var
      d : TMyData;
      s : TFileStream;
      c : TMyData;
     
    begin
     
      Try
        s := TFileStream.Create( './properties.dat', fmOpenRead );
        d := TMyData.Create(nil);
        d.StringList := TStringList.Create;
        s.ReadComponent(d);
     
        ShowMessage( Format( '| %s | %d | %d | %m |', [d.FieldString,d.FieldInteger,d.StringList.Count,d.FieldCurr] ) + #13 + d.StringList.Text );
     
        Try
          c := TMyData.Create(nil);
          c.StringList := TStringList.Create;
          // CRASH !
          s.ReadComponent(c);
      	Except
          s.Position := 0;
          // OK !
          s.ReadComponent(c);
          // La preuve !
          ShowMessage( Format( '| %s | %d | %d | %m |', [c.FieldString,c.FieldInteger,c.StringList.Count,c.FieldCurr] ) + #13 + c.StringList.Text );
      	end;
     
      Finally
        FreeAndNil(s);
        FreeAndNil(d);
        FreeAndNil(c);
      end;
     
    end;
     
     
    end.

  13. #13
    Membre régulier Avatar de TheFreeBerga
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    63
    Détails du profil
    Informations personnelles :
    Âge : 55
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 63
    Points : 77
    Points
    77
    Par défaut Dernière touche
    Afin de rendre ce mécanisme encore plus simple d'utilisation, j'ai écris un objet de base qui se charge lui-même de créer/supprimer/raz les champs qui nécessitent de l'être.
    A l'heure actuelle, je n'ai écris du code en ce sens que pour le TStringList mais ajouter d'autres objets est chose facile: une ligne à ajouter dans trois méthodes.
    J'aurai pu gérer les types simples (integer, currency, etc...) pour les initialiser à zéro à la création de l'objet mais je considère de la responsabilité du développeur que d'initialiser ses variables sans compter sur un comportement par défaut.

    Assez discuté, voici le code :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
     
    UNIT XChangeDataObject;
     
    {$mode objfpc}{$H+}
     
    INTERFACE
     
     
    USES
      Classes, SysUtils;
     
     
    TYPE
     
    	{ TXChangeData }
      TXChangeData  = Class( TComponent )
        PUBLIC
          constructor Create();
          destructor  Destroy(); override;
          procedure   Clear;
    	end;
     
      { Class de test }
      TXChangeDataTest  = Class( TXChangeData )
        PUBLIC
          OneString : String;
          OneInteger: Integer;
          OneTString: TStringList;
        PUBLISHED
          property A: String read OneString write OneString;
          property B: Integer read OneInteger write OneInteger;
          property C: TStringList read OneTString write OneTString;
    	end;
     
     
     
    IMPLEMENTATION
     
     
    USES
      typinfo,
      rttiutils
      ;
     
     
    { TXChangeData }
    constructor TXChangeData.Create();
    var
      PropList  : TPropInfoList;
      Loop      : Integer;
     
    begin
     
      inherited Create(nil);
     
      // Objet pour manipuler les propriétés
      PropList := TPropInfoList.Create( Self, [tkClass] );
     
      // Recherche les champs nécessitants d'etre initialisé
      for Loop := 0 to PropList.Count-1 do
      begin
        Case PropList.Items[Loop]^.PropType^.Name of
          'TStringList' : SetObjectProp( Self, PropList.Items[Loop]^.Name, TStringList.Create );
    		end;
    	end;
     
      FreeAndNil(PropList);
     
    end;
     
     
     
    destructor TXChangeData.Destroy();
    var
      PropList  : TPropInfoList;
      Loop      : Integer;
     
    begin
     
      // Objet pour manipuler les propriétés
      PropList := TPropInfoList.Create( Self, [tkClass] );
     
      // Recherche les champs nécessitants d'etre libéré
      for Loop := 0 to PropList.Count-1 do
      begin
        Case PropList.Items[Loop]^.PropType^.Name of
          'TStringList' : TStringList( GetObjectProp( Self, PropList.Items[Loop]^.Name ) ).Free;
    		end;
    	end;
     
      FreeAndNil(PropList);
     
      inherited Destroy;
     
    end;
     
     
     
    procedure TXChangeData.Clear();
    var
      PropList  : TPropInfoList;
      Loop      : Integer;
     
    begin
     
      // Objet pour manipuler les propriétés
      PropList := TPropInfoList.Create( Self, [tkClass] );
     
      // Recherche les champs nécessitants d'etre nettoyé
      for Loop := 0 to PropList.Count-1 do
      begin
        Case PropList.Items[Loop]^.PropType^.Name of
          'TStringList' : TStringList( GetObjectProp( Self, PropList.Items[Loop]^.Name ) ).Clear;
    		end;
    	end;
     
      FreeAndNil(PropList);
     
    end;
     
     
    END.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Copier les données d'un objet dans un autre
    Par franckcl dans le forum Langage
    Réponses: 6
    Dernier message: 19/06/2012, 12h19
  2. ORA:12899 : Tronquer automatiquement les données
    Par b_lob dans le forum Développement de jobs
    Réponses: 0
    Dernier message: 07/06/2011, 11h45
  3. Charger les données d'un objets
    Par rad_hass dans le forum ASP.NET
    Réponses: 2
    Dernier message: 17/12/2008, 14h58
  4. Réponses: 5
    Dernier message: 20/07/2008, 18h31

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo