Click here to Skip to main content
Click here to Skip to main content
Go to top

Deep copying (cloning) objects in Delphi

, 14 Dec 2012
Rate this:
Please Sign up or sign in to vote.
Deep copying (cloning) objects in Delphi.

When I first took a look at the prototype design pattern in GoF(years ago), I realized that there was a big obstacle (challenge) to implement it in Delphi: How to write a routine to really clone (not just recreate) an object? In other words, how to perform a deep-copy of a living object in Delphi.

There are approaches out there mimicking the deep copy by simply calling the constructor and reassigning the state of the object by hand (I don’t like it). There are others exposing that a deep copy could be accomplished for the descendants of TPersistent by calling the Assign method (I don’t like it either).

With the new RTTI extensions it seemed to me (and to others) that a deep copy could be accomplished using Reflection.

I was reluctant to write the routine myself since the work is not trivial. It could get really nasty because there might be composition, aggregation and God knows what within an arbitrary object.

So I waited….

Just a few days ago, I realized that I could use the JSON marshalling and unmarshalling features introduced in Delphi (2010?) to write the deep copy method. So I came with this:

.....
uses
  DBXJSON, DBXJSONReflect;
.....
 
function DeepCopy(aValue: TObject): TObject;
var
  MarshalObj: TJSONMarshal;
  UnMarshalObj: TJSONUnMarshal;
  JSONValue: TJSONValue;
begin
  Result:= nil;
  MarshalObj := TJSONMarshal.Create;
  UnMarshalObj := TJSONUnMarshal.Create;
  try
    JSONValue := MarshalObj.Marshal(aValue);
    try
      if Assigned(JSONValue) then
        Result:= UnMarshalObj.Unmarshal(JSONValue);
    finally
      JSONValue.Free;
    end;
  finally
    MarshalObj.Free;
    UnMarshalObj.Free;
  end;
end;

You can now use it like this:

.....
var
  MyObject1,
  MyObject2: TMyObject;
begin
  //Regular object construction
  MyObject1:= TMyObject.Create;

  //Deep copying an object
  MyObject2:= TMyObject(DeepCopy(MyObject1));

  try
    //Do something here

  finally
    MyObject1.Free;
    MyObject2.Free;
  end;
end;

I tested it with some complex cases and it seems to be working quite well. Anyhow, if you find any problems or limitations, please, let me know.

Now that you get the idea we can do more crazy things like patching TObject (or any other class hierarchy) by using helpers. Look at this:

.....
interface

uses
   DBXJSON, DBXJSONReflect;

type
  TObjectHelper = class helper for TObject
    function Clone: TObject;
  end;

implementation

function TObjectHelper.Clone: TObject;
var
  MarshalObj: TJSONMarshal;
  UnMarshalObj: TJSONUnMarshal;
  JSONValue: TJSONValue;
begin
  Result:= nil;
  MarshalObj := TJSONMarshal.Create;
  UnMarshalObj := TJSONUnMarshal.Create;
  try
    JSONValue := MarshalObj.Marshal(Self);
    try
      if Assigned(JSONValue) then
        Result:= UnMarshalObj.Unmarshal(JSONValue);
    finally
      JSONValue.Free;
    end;
  finally
    MarshalObj.Free;
    UnMarshalObj.Free;
  end;
end;

All of a sudden, TObject has a Clone method! Call it like this:

.....
var
  MyObject1,
  MyObject2: TMyObject;
begin
  //Regular object construction
  MyObject1:= TMyObject.Create;

  //Cloning an object
  MyObject2:= TMyObject(MyObject1.Clone);

  try
    //Do something here

  finally
    MyObject1.Free;
    MyObject2.Free;
  end;
end;

If you think that helpers are an aberration, you can still create a TCloneable class with a Clone method and inherit from it, right? You can even use the decorator pattern to attach a Clone method to an object. You can do more…Share it with me, please. Thanks!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

yanniel
Software Developer Digital Rapids
Canada Canada
My name is Yanniel Alvarez Alfonso. I was born in San Antonio de los Baños, Havana Province, Cuba on October 24th, 1982.
 
I majored in Information Technology Engineering at José Antonio Echeverría Polytechnic Institute (CUJAE) in Havana City, Cuba (July 2006). After that, I got a Masters Degree in Applied Computer Science at the same University (May 2009).
 
I used to work as a professor of Information Technology at CUJAE. Right now, I work as a Software Developer in Toronto, Canada. I moved to Canada under the Skilled Worker Program on February 26th, 2010.
 
This is my personal blog: Yanniel's notes; in which I write about miscellaneous topics.
 
The link at the end of this sentence compiles an index of all the articles I have written so far about Delphi Programming.

Comments and Discussions

 
QuestionType TMyObject is not known PinmemberMember 1053811720-Jan-14 23:11 
QuestionIs there any limitation? PinmemberMohammad Sanati10-Feb-13 19:22 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140921.1 | Last Updated 14 Dec 2012
Article Copyright 2012 by yanniel
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid