覆盖Assign方法的正确代码是什么?

时间:2017-08-29 07:26:53

标签: delphi

我对许多其他线程中的Assign代码感到困惑,例如,这个: See the Assign method implementation given in an answer

procedure TDispPitch.Assign(Source: TPersistent);
var
  LSource: TDispPitch;
begin
  if Source is TDispPitch then
  begin
    LSource := TDispPitch(Source);
    iLineSize := LSource.LineSize;
    iLineColor := LSource.LineColor; 
    bDisplayAccent := LSource.DisplayAccent; 
    bVisible := LSource.Visible; 
    Changed;
  end else
    inherited;
end;

但是如果对继承的调用是在Else部分,那么如何为满足if条件的对象分配基类属性?将此与VCL自己的TStringList代码进行比较。

procedure TStringList.Assign(Source: TPersistent);
begin
  inherited Assign(Source);
  if Source is TStringList then
  begin
    FCaseSensitive := TStringList(Source).FCaseSensitive;
    ...
  end;
end;

这首先正确调用Inherited,以便首先分配基类属性。

第一个代码块是对的吗?我在堆栈溢出的许多地方找到了这样的代码。我无法理解如何在该代码中分配基类属性。谁能解释一下?另一方面,如果代码错误,为什么没有人在所有这些线程中指出它?

1 个答案:

答案 0 :(得分:0)

最后我开始解决这个问题。实际上,当我在TCollection上实现Sort时,它没有工作,最后我能够看到错误以及它应该如何工作。我不得不查看旧Delphi XE4版本中的来源以确认我的发现。

以下是编写重写的Assign代码时需要考虑的重点:

  • TPersistent中的Assign不执行任何操作。实际上,如果派生类在需要时没有实现Assign覆盖,它会捕获错误。它会显示一条消息,上面写着“无法分配”。
  • 因此,如果您直接从TPersistent派生了一个类,那么您需要像顶部第一个示例那样的代码。对Inherited的调用并没有真正实现,因为分配的实际工作是由上面的If块完成的。我的集合项在第二个示例中有代码,即使它是从TCollectionItem派生的,它是一个TPersistent类,没有自己的Assign逻辑。因此,首先发生的对Inherited的调用会在排序尝试交换项目时立即导致异常“无法分配”。
  • 进一步说,如果你的类派生自一个基于TPersistent的中间类,你需要知道它是否有自己的Assign逻辑。如果是,则必须使用顶部的第二个示例,以便继承的Assign逻辑首先获得机会。在我的例子中,我有一个派生自TOwnedCollection / TCollection的类,它有自己的Assign逻辑,可以将Items从一个集合复制到另一个集合。所以我不得不在顶部使用第二个例子。

让我们尝试总结一下我们从使用TCollectionItem和TCollection / TOwnedCollection的角度学到的东西:

分配具有祖先TOwnedCollection / TCollection的类:使用顶部的第二个示例代码块,以便集合可以在您的之前进行分配。

分配具有祖先TCollectionItem / TPersistent的类:使用顶部的第一个示例代码块,因为这两个代码块都没有自己的Assigns。但是,如果从基于这些的另一个类派生并且它具有自己的Assign逻辑,则必须使用第二个示例,以便通过调用Inherited来首先执行其Assign。