2

在我的特定TPersistent类中,我想提供一个Clone函数,它返回对象的独立副本。

是否可以在后代中正常工作,而无需Clone在每个后代中实现该功能?

这与克隆任何未知字段或深度克隆(可以使用 RTTI 完成)无关。在下面我的最小示例中,您可以看到我想要放置Clone函数的位置。

由于它用于Assign()复制数据,因此它适用于任何后代。问题是构造函数,请参阅评论。如何调用该后代的正确构造函数?如果这很难做到,可以假设没有一个后代在没有覆盖的情况下覆盖构造函数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

所以,正确的类出来了,它在这个最小的例子中正常工作,但不幸的是,我必要的初始化没有完成(应该出现两次)。

我希望我能说清楚,你喜欢我的示例代码:) - 我也很感激任何其他评论或改进。我的Assign()实施好吗?

4

2 回答 2

2

您不需要将非泛型基类构造函数抽象化。您可以在那里实现克隆,因为您有一个虚拟构造函数。

此外,您不需要将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返回TClass. 我们将其转换TMyClassBaseClass为确保我们调用您的基类虚拟构造函数。

我也不明白您为什么TMyClassBase<T>要从中制作抽象和派生规范。您应该能够在泛型类中实现所需的一切。

于 2018-03-22T21:48:43.277 回答
-2

这似乎做到了:

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 测试。请给出意见 :)

于 2018-03-22T17:38:00.390 回答