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!