Replication

I must say, I am still pretty exited by the extended RTTI in Delphi 2010. It makes life so much easier in many circumstances. “Cloning” (I call it replication) is one of those. Say hello to TReplicator<T> (in DeHL.Replication). It can take any type and create an exact copy (including deep cloning for complex types).

The following example should pretty much explain what and how:

uses
  SysUtils,
  DeHL.Replication,
  DeHL.Collections.List;

type
  { Define a test class }
  TData = class
    { Pointer to self }
    FSelf: TObject;

    { A chunck of bytes }
    FSomeData: TBytes;

    { Some dummy string }
    FName: string;

    { An internal pointer. Marked as non-replicable -- will not be copied }
    [NonReplicable]
    FInternalPtr: Pointer;

    { This field will be copied by reference. It means that the new
      object will have the reference to the same TList<String> and not
      a copy of it. }
    [ByReference]
    FList: TList<String>;
  end;

var
  LInput, LOutput: TData;
  LReplicator: TReplicator<TData>;
begin
  { Create the input }
  LInput := TData.Create;
  LInput.FSelf := LInput;
  LInput.FSomeData := TBytes.Create(1, 2, 3, 4, 5, 6);
  LInput.FName := 'Some Name';
  LInput.FInternalPtr := Ptr($DEADBABE);
  LInput.FList := TList<String>.Create(['Hello', 'World']);

  { Create the replicator }
  LReplicator := TReplicator<TData>.Create;

  { Create a copy of LInput }
  LReplicator.Replicate(LInput, LOutput);

  { ... }
end.

As you can see we take LInput and create a copy into LOutput. Also note the use of [NonReplicable] and [ByReference] attributes on two fields of TData class. NonReplicable forces the replication process to skip the field and ByReference forces a shallow copy of reference fields (objects, pointers to arrays and dynamic arrays).

For the ease of use, a TCloneableObject is defined in the same unit (DeHL.Replication). TCloneableObject provides a method called Clone() which returns a copy of the object:

uses
  SysUtils,
  DeHL.Replication;

type
  { Define a test class }
  TTestMe = class(TCloneableObject)
    { ... }
  end;

var
  LInput, LOutput: TTestMe;
begin
  { ... }
  LOutput := LInput.Clone() as TTestMe;
  { ... }
end.

Note: This is just a preview of a new feature finding it’s way into DeHL. It’s currently only in the SVN repository. As usual, I need to write unit tests and so on to make sure it actually has no bugs (or at leas not that many bugs ;).