Manière correcte de dupliquer l'objet Delphi

Quels sont les avantages et les inconvénients de la duplication d'une instance d'objet avec un constructeur ou une fonction d'instance?

Exemple:

type
  TMyObject = class
  strict private
    FField: integer; 
  public
    constructor Create(srcObj: TMyObject); overload; 
    //alternatively:
    //constructor CreateFrom(srcObj: TMyObject);
    property Field: integer read FField;
  end;

constructor TMyObject.Create(srcObj: TMyObject);
begin
  inherited Create;
  FField := srcObj.Field;
end;

Exemple B:

type
  TMyObject = class
  strict private
    FField: integer; 
  public
    function Clone: TMyObject;
    property Field: integer read FField;
  end;

function TMyObject.Clone: TMyObject;
begin
  Result := TMyObject.Create;
  Result.FField := FField;
end;

Une différence majeure vient immédiatement à l'esprit - dans ce dernier cas, le constructeur Create devrait être virtuel afin qu'une hiérarchie de classes supportant le Clone puisse être construite en se basant sur le TMyObject.

Supposons que ce n'est pas un problème - que TMyObject et tout ce qui en est basé est entièrement sous mon contrôle. Quelle est votre façon préférée de faire copy constructor dans Delphi? Quelle version trouvez-vous la plus lisible? Quand utiliseriez-vous l'ancienne ou la dernière approche? Discuter. :)

Modifier: Ma principale préoccupation avec le premier exemple est que l'utilisation est très lourde par rapport à la deuxième approche, c'est-à-dire

newObj := TMyObject.Create(oldObj)

Vs.

newObj := oldObj.Clone;

EDIT2 ou "pourquoi je veux une opération sur une seule ligne"

Je conviens que L'affectation est une approche raisonnable dans la plupart des cas. Il est même raisonnable d'implémenter 'copy constructor' en interne en utilisant simplement attribuer.

Je crée généralement de telles copies lors du multithreading et du passage d'objets dans la file d'attente des messages. Si la création d'objet est rapide, je passe généralement une copie de l'objet d'origine car cela simplifie vraiment les problèmes de propriété de l'objet.

IOW, je préfère écrire

Send(TMyObject.Create(obj));

Ou

Send(obj.Clone);

À

newObj := TMyObject.Create;
newObj.Assign(obj);
Send(newObj);
21
demandé sur gabr 2010-10-28 14:05:33

3 réponses

Le premier ajoute des informations sur l'objet à créer, le second non. Cela peut être utilisé pour instancier par exemple un descendant ou un ancêtre d'une classe

La voie Delphi (TPersistent) sépare la création et le clonage:

dest := TSomeClass.Create; 
dest.Assign(source);  

Et a cette même propriété que vous choisissez explicitement la classe à instancier. Mais vous n'avez pas besoin de deux constructeurs, un pour une utilisation normale, et un où vous voulez cloner.

Modifier en raison de l'exigence oneline Vous pouvez le mélanger bien sûr utiliser des métaclasses Delphi (non testées)

type
  TBaseSomeObject = class;
  TBaseObjectClass = class of TBaseSomeObject;

  TBaseSomeObject = class(TPersistent)
    function Clone(t: TBaseObjectClass = nil): TBaseSomeObject; virtual;
  end;

...

  function TBaseSomeObject.Clone(t: TBaseObjectClass = nil): TBaseSomeObject;
  begin
    if Assigned(t) then
      Result := t.Create
    else
      Result := TBaseObjectClass(Self.ClassType).Create;
    Result.Assign(Self);
  end;


 SendObject(obj.Clone); // full clone.
 SendObject(obj.Clone(TDescandantObject)); // Cloned into Descendant object 

Pour le reste, implémentez simplement vos opérateurs assign(), et vous pouvez mélanger plusieurs façons.

Edit2

J'ai remplacé le code ci - dessus par le code testé dans D2009. Il y a quelques dépendances des types qui pourraient vous avoir confus, j'espère que c'est plus clair de cette façon. Bien sûr, vous devrez étudier le mécanisme assign. J'ai également testé le paramètre par défaut metaclass=nil et cela fonctionne, donc je l'ai ajouté.

27
répondu Marco van de Voort 2015-10-27 14:02:25

Je ne pense pas qu'il y ait une bonne façon de dépendre simplement du style personnel. (Et comme Marco l'a souligné, il y a plus de moyens.)

  • la manière du constructeur est courte mais elle viole le principe selon lequel le constructeur ne doit construire que l'objet. Ce qui n'est peut-être pas un problème.
  • la manière de clone est courte bien que vous ayez besoin de fournir un appel pour chaque classe.
  • la manière assign est plus Delphi. Il sépare la création et l'initialisation ce qui est bien parce que nous aimons celui méthode un concept de fonction qui rend le code mieux à maintenir.

Et si vous implémentez Assign using streams, vous n'avez qu'un seul endroit pour vous soucier des champs qui doivent être disponibles.

6
répondu Toon Krijthe 2018-02-07 16:15:18

J'aime le style clone - mais seulement en Java (ou tout autre langage GC). Je l'ai utilisé quelques fois en Delphes, mais surtout je reste avec Create et Assign, car il est beaucoup plus clair qui est responsable de la destruction de l'objet.

3
répondu splash 2010-10-28 11:08:54