【问题标题】:A simple way to clone a complex object克隆复杂对象的简单方法
【发布时间】:2020-11-03 19:40:20
【问题描述】:

我有一堂课,看起来像这样:

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;      

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

【问题讨论】:

  • 使用Assign 确实是正确的方法。但是你必须在你创建的每个新类中覆盖Assign
  • 仅供参考,Delphi RTL 已经有一个 TCharacter 类,因此您应该考虑将您的类重命名为其他更独特的名称。

标签: delphi clone assign


【解决方案1】:

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

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...

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 当然,Assign 并不存在于从TObject 派生的每个类中,而只存在于TPersistent 的后代中。
    • "别忘了调用inherited" - 除非下一个实现Assign() 的祖先是TPersistent 本身并且你的类没有实现AssignTo()TPersistent.Assign() 调用AssignTo(),如果AssignTo() 未被覆盖,则TPersistent.AssignTo() 将引发EConvertError 异常。
    猜你喜欢
    • 2010-11-19
    • 2013-04-06
    • 2014-07-02
    • 2017-04-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-23
    • 1970-01-01
    相关资源
    最近更新 更多