在我的特定类

时间:2018-03-22 17:19:40

标签: delphi generics clone tpersistent

在我的特定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()实施是否正常?

2 个答案:

答案 0 :(得分:2)

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

此外,您不需要将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>抽象和衍生的规范。您应该能够在泛型类中实现所需的一切。

答案 1 :(得分:-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进行测试。请评论:)