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

Langage Delphi Discussion :

Copier les données d'un objet dans un autre


Sujet :

Langage Delphi

  1. #1
    Membre averti Avatar de franckcl
    Homme Profil pro
    Developpeur Delphi
    Inscrit en
    Septembre 2004
    Messages
    516
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Developpeur Delphi
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Septembre 2004
    Messages : 516
    Points : 443
    Points
    443
    Par défaut Copier les données d'un objet dans un autre
    Bonjour,

    N'Existe-t-il pas une manière automatique pour copier les données d'un objet dans un autre ?
    De quelle classe faut-il hériter pour que cela soit possible ?

    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
     
    type
    TMyTag = class
      Nom : String;
      Valeur : integer;
      Etat : boolean;
    End;
     
    (...)
    Var 
     Tag1,Tag2:TMyTag;
    Begin
      Tag1:=TMyClass.create;
      Tag1.Nom:='toto';
      Tag1.Valeur:=12;
      Tag1.Etat:=false;
      Tag2:=TMyClass.create;
      Tag2:=Tag1;
    OU
      Tag2.Assign(Tag1);
    J'ai bien compris qu'on pouvait écrire sa propre procedure "Assign" mais ça devient lourd lorsqu'on a énormément de champs à affecter surtout pour la maintenance du logiciel lorsqu'il faut rajouter ou modifier des champs.

    Quelles sont les solutions ?
    merci
    franck

  2. #2
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 586
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 586
    Points : 25 262
    Points
    25 262
    Par défaut
    Tu peux aussi tenter les RTTI pour faire une fonction Assign générique
    Si tu n'avais que des types simples comme integer, boolean, char[], tu aurais pu tenter un CopyMemory mais avec le string, c'est risque de violation d'accès lié à une libération innatendue (mon collègue qui migre DCOM en DATASNAP en C++Builder XE2 a eu cette mauvaise surprise quand j'ai vu son memcpy, j'ai tout de suite compris sa VA)

    Désolé, j'ai un code similaire Assign et SetPublishedPropertiesFromPersistent mais uniquement en C++Builder, ça se traduit facilement
    En Delphi, j'ai juste la fonction GetPersistentProperties

    Code c++ : 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
     
    //---------------------------------------------------------------------------
    /*override*/void __fastcall TShaiORPersistent::Assign(TPersistent* Source)
    {
      this->PrimaryID = -1;
     
      if (Source->InheritsFrom(__classid(TShaiORPersistent)))
      {
        this->DataSource = ((TShaiORPersistent*)Source)->DataSource;
        if ( ! this->DataSource)
          SetPublishedPropertiesFromPersistent(Source);
      }
      else if (Source->InheritsFrom(__classid(TDataSet)))
      {
        this->DataSource = NULL;
        SetPublishedPropertiesFromDataSet((TDataSet*)Source);
      }
      else
        inherited::Assign(Source);
    }
     
    //---------------------------------------------------------------------------
    /**
     * SetPublishedPropertiesFromDataSet fill object with DataSet
     * @return number of affected properties (-1 on null or empty DataSet)
     */
    int TShaiORPersistent::SetPublishedPropertiesFromDataSet(TDataSet* ADataSet)
    {
      int Result = -1;
      if (ADataSet && ! ADataSet->IsEmpty())
      {
        Result = 0;
        for (int i = 0; i < ADataSet->FieldCount; i++)
        {
          TField *Field = ADataSet->Fields->Fields[i];
          AnsiString PropertyName = PropertyNameByFieldName(Field->FieldName);
          if ( ! PropertyName.IsEmpty())
          {
            // Certaines propriétés publiées peuvent être en Lecture Seule, le chargement doit se faire manuellement !
            if (Typinfo::GetPropInfo(this, PropertyName)->SetProc)
            {
              Variant CopyValue = Field->Value;
              // Une Gestion minimale du Null, le type Variant permettra de gérer pleinement cet état au besoin !
              if (Field->IsNull)
              {
                switch (TShaiRTTIWrapper::GetPersistentType(this->ClassType(), PropertyName))
                {
                  // Variant reste Intact car gère naturellement le Null
     
                  case tkInteger :
                  case tkFloat:
                  case tkInt64 :
                    CopyValue = 0;
                    break;
     
                  case tkString :
                  case tkLString :
                  case tkWString :
                    CopyValue = "";
                    break;
                }
              }
              // Remarques :
              // - Ne pas utiliser de passage de paramètre par référence dans les Setter, SetPropValue ou SetStrProp ne les gère pas !
              // - L'ordre des constructeurs et de l'instanciation statique des membres d'un objet C++ est par itération successive,
              //   la conséquence est qu'il ne faut pas utiliser String statique mais bien String* dynamique pour contourner ce mécanisme !
              //   C'est d'ailleurs, ce phénomène qui empêche de bénéficier pleinement des méthodes virtuelles dans un constructeur (cela dépend si TObject ou pas !)
              // Intervention sur les Forums en tant que ShaiLeTroll :
              // - Virtual - www.developpez.net/forums/d1059827/c-cpp/outils-c-cpp/cppbuilder/methodes-virtuelles-appeles-constructeur/
              Typinfo::SetPropValue(this, PropertyName, CopyValue);
              Result++;
            }
          }
        }
      }
      return Result;
    }
     
    //---------------------------------------------------------------------------
    /**
     * SetPublishedPropertiesFromPersistent fill object with another object
     * @return number of affected properties (-1 on null object)
     */
    int TShaiORPersistent::SetPublishedPropertiesFromPersistent(TPersistent* APersistent)
    {
      int Result = -1;
      if (APersistent)
      {
        Result = 0;
     
        TStringList *PropList = new TStringList();
        try
        {
          if (TShaiRTTIWrapper::GetPersistentProperties(APersistent->ClassType(), PropList))
          {
            for (int i = 0; i < PropList->Count; i++)
            {
              AnsiString PropertyName = PropList->Strings[i];
     
              // Certaines propriétés publiées peuvent être en Lecture Seule, le chargement doit se faire manuellement !
              if (Typinfo::IsPublishedProp(this, PropertyName) && GetPropInfo(this, PropertyName)->SetProc)
              {
                Typinfo::SetPropValue(this, PropertyName, GetPropValue(APersistent, PropertyName, false));
                Result++;
              }
            }
          }
        }
        __finally
        {
          delete PropList;
        }
      }
     
      return Result;
    }

    Code c++ : 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
    //---------------------------------------------------------------------------
    //                            TShaiRTTIWrapper                             -
    //---------------------------------------------------------------------------
    /*static*/ bool TShaiRTTIWrapper::GetPersistentProperties(TClass AClass, TStrings* List, bool PersistentOnly/* = true*/)
    {
      Typinfo::PPropInfo PropInfo;
     
      bool Result = List && ( ! PersistentOnly || (PersistentOnly && AClass->InheritsFrom(__classid(TPersistent))));
      if (Result)
      {
        // Nettoyage de la Liste
        List->Clear();
        // Récupération des Informations RTTI de la Classe
        PTypeInfo TypeInfo = (PTypeInfo)AClass->ClassInfo();
        // Obtention du Nombre de Propriété de la Classe
        int Count = Typinfo::GetTypeData(TypeInfo)->PropCount;
        Result = Count;
        if (Count)
        {
          // Allocation de la mémoire pour la Liste des Propriétés de la Classe
          Typinfo::PPropList PropList = (Typinfo::PPropList)malloc(Count * sizeof(Typinfo::PPropList));
          try
          {
            // Récupération du Tableur de Pointeur décrivant les Propriétés de la Classe
            Typinfo::GetPropInfos(TypeInfo, PropList);
            Typinfo::PPropInfo PropInfo;
            for (int i = 0; i < Count; i++)
            {
              // Récupération du Ieme Element décrivant l'une des Propriétés de la Classe
              PropInfo = (*PropList)[i];
              // Ajout du Nom de l'une des Propriétés de la Classe dans la Liste
              if (PropInfo)
                List->Add(PropInfo->Name);
            }
          }
          __finally
          {
            // Libération de la mémoire pour la Liste des Propriétés de la Classe
            free(PropList);
          }
        }
      }
     
      return Result;
    }

  3. #3
    Membre averti Avatar de franckcl
    Homme Profil pro
    Developpeur Delphi
    Inscrit en
    Septembre 2004
    Messages
    516
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Developpeur Delphi
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Septembre 2004
    Messages : 516
    Points : 443
    Points
    443
    Par défaut
    je n'ai que des types simples effectivement.
    Avant de créer cette classe, j'utilisais un type record et pour copier les données entre une variable de type record et un pointeur sur un record (alloué par un new) j'ai utilisé la fonction copyMemory mais cela générait une exception lorsque je quittais l'application, c'est pouquoi j'ai voulu exploiter une classe.
    Mais bon, c'est fait j'ai écrit une procedure assign, c'est plus propre même si c'est plus ennuyeux à maintenir.

  4. #4
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 457
    Points
    28 457
    Par défaut
    attention, le type "string" n'est pas simple, sauf à utiliser un ShortString.

    mais si tu peux librement adapter ta structure tu peux regrouper tout ce qui est "simple"
    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
     
    type
      TData = record
        Valeur: Integer;
        Etat  : Boolean;
      end;
     
      TMyTag = class
       private
         FNom: string;
         FData: TData;
       public
        procedure Assign(AValue: TMyTag);
        property Nom: string read FNom;
        property Valeur: Integer read FData.Valeur;
        property Etat: Boolean read FData.Etat;
       end;
     
    procedure TMyTag.Assign(AValue: TMyTag);
    begin 
      FNom := AValue.FNom;
      FData := AValue.FData; // copie tout le reste d'un coup
    end;

  5. #5
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 586
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 586
    Points : 25 262
    Points
    25 262
    Par défaut
    L'affectation d'un Record contenant une string ne pose aucun problème, cela gère parfaitement le compteur de référence !

    Paul, tu as du confondre avec ça (surement le problème de franckcl)

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    procedure TMyTag.Assign(AValue: TMyTag);
    begin 
      FNom := AValue.FNom;
      CopyMemory(@FData, @AValue.FData, sizeof(FData)); // copie tout le reste d'un coup SANS précaution !
    end;
    en reprenant le code de Paul :

    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
    type
      TData = record
        FNom: string;
        Valeur: Integer;
        Etat  : Boolean;
      end;
     
      TMyTag = class
       private
         FData: TData;
       public
        procedure Assign(AValue: TMyTag);
        property Nom: string read FNom;
        property Valeur: Integer read FData.Valeur;
        property Etat: Boolean read FData.Etat;
       end;
     
    procedure TMyTag.Assign(AValue: TMyTag);
    begin 
      FData := AValue.FData; // copie tout et gère les types somme string et dyn array
    end;
    et pour aller plus loin encore avec de la mémoire gérée manuellement

    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
     
     
    type
      TMyTag = class;
     
      TData = record
        FNom: string;
        Valeur: Integer;
        Etat  : Boolean;
        Buffer: PByte;
        BufSize: Integer;
        Next: TMyTag; 
      end;
     
      TMyTag = class
       private
         FData: TData;
         function GetBuffer(Index: Integer): Byte; // imagine un accès au FData.Buffer
       public
        procedure Assign(AValue: TMyTag);
        property Nom: string read FNom;
        property Valeur: Integer read FData.Valeur;
        property Etat: Boolean read FData.Etat;
        property Buffer[Index: Integer]: Byte read GetBuffer;
       end;
     
    procedure TMyTag.Assign(AValue: TMyTag);
    begin 
       if Assigned(AValue) then
       begin
      FData := AValue.FData; // copie tout et gère les types somme string et dyn array
         FData.Buffer := nil; 
         if FData.BufSize > 0 then
         begin
           GetMem(FData.Buffer, FData.BufSize); // FreeMem dans le Destructor !
           CopyMemory(@FData.Buffer, @AValue.FData.Buffer, FData.BufSize);
         end;
     
         if Assigned(AValue.Next) then // ça évite un CALL même si le Assign le gère déjà !
           FData.Next.Assign(AValue.Next);
      end;  
    end;

  6. #6
    Membre averti Avatar de franckcl
    Homme Profil pro
    Developpeur Delphi
    Inscrit en
    Septembre 2004
    Messages
    516
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Developpeur Delphi
    Secteur : High Tech - Électronique et micro-électronique

    Informations forums :
    Inscription : Septembre 2004
    Messages : 516
    Points : 443
    Points
    443
    Par défaut
    Merci pour toutes ces infos très intéressantes.

  7. #7
    Expert éminent sénior
    Avatar de Paul TOTH
    Homme Profil pro
    Freelance
    Inscrit en
    Novembre 2002
    Messages
    8 964
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 55
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Freelance
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Novembre 2002
    Messages : 8 964
    Points : 28 457
    Points
    28 457
    Par défaut
    Citation Envoyé par ShaiLeTroll Voir le message
    L'affectation d'un Record contenant une string ne pose aucun problème, cela gère parfaitement le compteur de référence !
    diantre, je vieillis

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

Discussions similaires

  1. Réponses: 6
    Dernier message: 06/06/2014, 21h21
  2. [AC-2003] copier les données d'un texte_box dans une liste_box
    Par facteur dans le forum VBA Access
    Réponses: 4
    Dernier message: 17/04/2014, 14h33
  3. Copier les données de plusieurs colonnes dans une nouvelle feuille
    Par lolonico1974 dans le forum Macros et VBA Excel
    Réponses: 2
    Dernier message: 07/07/2010, 07h51
  4. [XL-2007] Copier les données d'une cellule dans une Application non Office en VBA
    Par EmmanuelleC dans le forum Macros et VBA Excel
    Réponses: 11
    Dernier message: 11/05/2010, 14h27
  5. [FLASH MX] Copier un MC d'un objet dans un autre
    Par hanane28 dans le forum Flash
    Réponses: 4
    Dernier message: 24/12/2004, 13h22

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