克隆复杂对象的简单方法

A simple way to clone a complex object

我有一个 class,看起来像这样:

TCharacter = class (TData)
  Images:    array of T_Variable_Image;
  Description:        String;
  Place:              TPlace;   
  Dialogues:          TSequenceData;    
  Abilities: array of TAbility;       
  Inventory:          TContainer;       
  Journal:            TJournal;        
  SideName:           String;
  Side:               Integer;                 
  Status:             String;                
  Disabled:           Boolean;      

正如您所看到的,它的一半字段是其他 classes 或记录,其中许多具有类似的复杂结构。 TCharacter 继承自 TData,后者又继承自 TPersistent - 我认为它的 Assign 方法可以帮助我轻松地将 TCharacter 的一个实例克隆到另一个实例中。 las,这不是那么容易。所以我的问题是 - 在不丢失任何数据或创建指针的情况下克隆实例的最佳方法是什么,它会随原始实例一起改变。

覆盖您 class 中的 Assign 方法,分配您引入的新 fields/properties。不要忘记调用继承。 如果字段是 classes,您应该调用它们的 Assign 方法,如果您创建了这些 classes,请不要忘记重写它们的 Assign 方法。

TPersistent.Assign/To() 方法正是您要找的。例如,您只需要在各种 class 中 实施 它(可能需要一些调整,具体取决于您的实际 class 设计):

type
  TData = class (TPersistent)
    ...
    function Clone: TData;
  end;

  TDataClass = class of TData;

  T_Variable_Image = class (TData)
    ...
    procedure Assign(Source: TPersistent); override;
  end;

  TPlace = class (TData)
    ...
    procedure Assign(Source: TPersistent); override;
  end;

  TSequenceData = class (TData)
    ...
    procedure Assign(Source: TPersistent); override;
  end;

  TAbility = class (TData)
    ...
    procedure Assign(Source: TPersistent); override;
  end;

  TContainer = class (TData)
    ...
    procedure Assign(Source: TPersistent); override;
  end;

  TJournal = class (TData)
    ...
    procedure Assign(Source: TPersistent); override;
  end;        

  TCharacter = class (TData)
    Images:    array of T_Variable_Image;
    Description:        String;
    Place:              TPlace;   
    Dialogues:          TSequenceData;    
    Abilities: array of TAbility;       
    Inventory:          TContainer;       
    Journal:            TJournal;        
    SideName:           String;
    Side:               Integer;                 
    Status:             String;                
    Disabled:           Boolean;      
    ...
    procedure Assign(Source: TPersistent); override;
  end;

...

function TData.Clone: TData;
begin
  Result := TDataClass(ClassType).Create;
  try
    Result.Assign(Self);
  except
    Result.Free;
    raise;
  end;
end;

...

procedure TCharacter.Assign(Source: TPersistent);
var
  Src: TCharacter;
  I : Integer;
begin
  if Source is TCharacter then
  begin
    Src := TCharacter(Source);

    for I := Low(Images) to High(Images) do
    begin
      Images[I].Free;
    end;
    SetLength(Images, Length(Src.Images));
    for I := Low(Src.Images) to High(Src.Images) do
    begin
      Images[I] := T_Variable_Image(Src.Images[I].Clone);
    end;

    Description := Src.Description;
    Place.Assign(Src.Place);
    Dialogues.Assign(Src.Dialogues);

    for I := Low(Abilities) to High(Abilities) do
    begin
      Abilities[I].Free;
    end;
    SetLength(Abilities, Length(Src.Abilities));
    for I := Low(Src.Abilities) to High(Src.Abilities) do
    begin
      Abilities[I] := TAbility(Src.Abilities[I].Clone);
    end;

    Inventory.Assign(Src.Inventory);
    Journal.Assign(Src.Journal);
    SideName := Src.SideName;
    Side := Src.Side;
    Status := Src.Status;
    Disabled = Src.Disabled;
  end else
    inherited;
end;

// implement Assign() for your other classes as needed...