在我的特定 类 中实现 Clone() 的最佳方式
Best way to implement Clone() in my specific classes
在我的特定 TPersistent
classes 中,我想提供一个 Clone
函数,它 returns 对象的独立副本。
是否可以在不在每个后代中实现 Clone
函数的情况下使它与后代一起正常工作?
这与克隆任何未知字段或深度克隆(可以使用 RTTI 完成)无关。在我下面的最小示例中,您可以看到我想要放置 Clone
函数的位置。
由于它使用 Assign()
复制数据,因此它适用于任何后代。问题是构造函数,请参阅注释。我如何调用该后代的正确构造函数?如果这很难做到,可以假设 none 的后代也覆盖了构造函数而不覆盖 Clone
。
program Test;
uses System.SysUtils, System.Classes;
type
TMyClassBase = class abstract(TPersistent)
public
constructor Create; virtual; abstract;
function Clone: TMyClassBase; virtual; abstract;
end;
TMyClassBase<T> = class abstract(TMyClassBase)
private
FValue: T;
public
constructor Create; overload; override;
function Clone: TMyClassBase; override;
procedure Assign(Source: TPersistent); override;
property Value: T read FValue write FValue;
end;
TMyClassInt = class(TMyClassBase<Integer>)
public
function ToString: string; override;
end;
TMyClassStr = class(TMyClassBase<string>)
public
function ToString: string; override;
end;
constructor TMyClassBase<T>.Create;
begin
Writeln('some necessary initialization');
end;
procedure TMyClassBase<T>.Assign(Source: TPersistent);
begin
if Source is TMyClassBase<T> then FValue:= (Source as TMyClassBase<T>).FValue
else inherited;
end;
function TMyClassBase<T>.Clone: TMyClassBase;
begin
{the following works, but it calls TObject.Create!}
Result:= ClassType.Create as TMyClassBase<T>;
Result.Assign(Self);
end;
function TMyClassInt.ToString: string;
begin
Result:= FValue.ToString;
end;
function TMyClassStr.ToString: string;
begin
Result:= FValue;
end;
var
ObjInt: TMyClassInt;
ObjBase: TMyClassBase;
begin
ObjInt:= TMyClassInt.Create;
ObjInt.Value:= 42;
ObjBase:= ObjInt.Clone;
Writeln(ObjBase.ToString);
Readln;
ObjInt.Free;
ObjBase.Free;
end.
输出为
some necessary initialization
42
所以,正确的 class 出来了,它在这个最小的例子中工作正常,但不幸的是,我的 必要的初始化 没有完成(应该出现两次) .
我希望我能说清楚并且您喜欢我的示例代码:) - 我也非常感谢任何其他评论或改进。 我的 Assign()
实现是否正常?
这似乎可以做到:
function TMyClassBase<T>.Clone: TMyClassBase;
begin
{create new instance using empty TObject constructor}
Result:= ClassType.Create as TMyClassBase<T>;
{execute correct virtual constructor of descendant on instance}
Result.Create;
{copy data to instance}
Result.Assign(Self);
end;
但是,我以前从未见过 - 感觉非常非常错误...
我验证它正确地初始化了目标对象的数据并且确实调用了后代构造函数一次。我看没问题,也没有报告内存泄漏。使用 Delphi 10.2.2 测试。请评论:)
您不需要使 non-generic 基础 class 构造函数抽象。你可以在那里实现克隆,因为你有一个虚拟构造函数。
此外,您不需要使 Clone
方法成为虚拟方法。
type
TMyClassBase = class abstract(TPersistent)
public
constructor Create; virtual; abstract;
function Clone: TMyClassBase;
end;
...
type
TMyClassBaseClass = class of TMyClassBase;
function TMyClassBase.Clone: TMyClassBase;
begin
Result := TMyClassBaseClass(ClassType).Create;
try
Result.Assign(Self);
except
Result.DisposeOf;
raise;
end;
end;
请注意 ClassType
returns TClass
。我们将其转换为 TMyClassBaseClass
以确保我们调用您的基础 class 虚拟构造函数。
我也不明白你为什么要从中提取 TMyClassBase<T>
抽象和派生规范。您应该能够在通用 class 中实现您需要的一切。
在我的特定 TPersistent
classes 中,我想提供一个 Clone
函数,它 returns 对象的独立副本。
是否可以在不在每个后代中实现 Clone
函数的情况下使它与后代一起正常工作?
这与克隆任何未知字段或深度克隆(可以使用 RTTI 完成)无关。在我下面的最小示例中,您可以看到我想要放置 Clone
函数的位置。
由于它使用 Assign()
复制数据,因此它适用于任何后代。问题是构造函数,请参阅注释。我如何调用该后代的正确构造函数?如果这很难做到,可以假设 none 的后代也覆盖了构造函数而不覆盖 Clone
。
program Test;
uses System.SysUtils, System.Classes;
type
TMyClassBase = class abstract(TPersistent)
public
constructor Create; virtual; abstract;
function Clone: TMyClassBase; virtual; abstract;
end;
TMyClassBase<T> = class abstract(TMyClassBase)
private
FValue: T;
public
constructor Create; overload; override;
function Clone: TMyClassBase; override;
procedure Assign(Source: TPersistent); override;
property Value: T read FValue write FValue;
end;
TMyClassInt = class(TMyClassBase<Integer>)
public
function ToString: string; override;
end;
TMyClassStr = class(TMyClassBase<string>)
public
function ToString: string; override;
end;
constructor TMyClassBase<T>.Create;
begin
Writeln('some necessary initialization');
end;
procedure TMyClassBase<T>.Assign(Source: TPersistent);
begin
if Source is TMyClassBase<T> then FValue:= (Source as TMyClassBase<T>).FValue
else inherited;
end;
function TMyClassBase<T>.Clone: TMyClassBase;
begin
{the following works, but it calls TObject.Create!}
Result:= ClassType.Create as TMyClassBase<T>;
Result.Assign(Self);
end;
function TMyClassInt.ToString: string;
begin
Result:= FValue.ToString;
end;
function TMyClassStr.ToString: string;
begin
Result:= FValue;
end;
var
ObjInt: TMyClassInt;
ObjBase: TMyClassBase;
begin
ObjInt:= TMyClassInt.Create;
ObjInt.Value:= 42;
ObjBase:= ObjInt.Clone;
Writeln(ObjBase.ToString);
Readln;
ObjInt.Free;
ObjBase.Free;
end.
输出为
some necessary initialization
42
所以,正确的 class 出来了,它在这个最小的例子中工作正常,但不幸的是,我的 必要的初始化 没有完成(应该出现两次) .
我希望我能说清楚并且您喜欢我的示例代码:) - 我也非常感谢任何其他评论或改进。 我的 Assign()
实现是否正常?
这似乎可以做到:
function TMyClassBase<T>.Clone: TMyClassBase;
begin
{create new instance using empty TObject constructor}
Result:= ClassType.Create as TMyClassBase<T>;
{execute correct virtual constructor of descendant on instance}
Result.Create;
{copy data to instance}
Result.Assign(Self);
end;
但是,我以前从未见过 - 感觉非常非常错误...
我验证它正确地初始化了目标对象的数据并且确实调用了后代构造函数一次。我看没问题,也没有报告内存泄漏。使用 Delphi 10.2.2 测试。请评论:)
您不需要使 non-generic 基础 class 构造函数抽象。你可以在那里实现克隆,因为你有一个虚拟构造函数。
此外,您不需要使 Clone
方法成为虚拟方法。
type
TMyClassBase = class abstract(TPersistent)
public
constructor Create; virtual; abstract;
function Clone: TMyClassBase;
end;
...
type
TMyClassBaseClass = class of TMyClassBase;
function TMyClassBase.Clone: TMyClassBase;
begin
Result := TMyClassBaseClass(ClassType).Create;
try
Result.Assign(Self);
except
Result.DisposeOf;
raise;
end;
end;
请注意 ClassType
returns TClass
。我们将其转换为 TMyClassBaseClass
以确保我们调用您的基础 class 虚拟构造函数。
我也不明白你为什么要从中提取 TMyClassBase<T>
抽象和派生规范。您应该能够在通用 class 中实现您需要的一切。