在我的特定 类 中实现 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 中实现您需要的一切。