克隆复杂对象的简单方法
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...
我有一个 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...