【问题标题】:What is the correct code to override Assign method?覆盖分配方法的正确代码是什么?
【发布时间】:2017-08-29 07:26:53
【问题描述】:

我对许多其他线程中的分配代码感到困惑,例如,这个: 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;

但是如果对inherited的调用在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,以便首先分配基类属性。

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

【问题讨论】:

  • 这取决于是否有任何基类属性。无法判断从其上下文中删除的第一个代码示例。您显然对所有部件的工作方式有一个完美的了解。我相信你可以自己评估代码。
  • 感谢您的意见。我想从最佳实践的角度来理解它。我认为上面的 VCL 代码总是可以工作的。我不明白的是第一个代码中“else”的使用。总是先调用继承有什么害处?我想我需要了解流媒体系统如何使用它。所有讨论基于 TCollectionItem 和 TOwnedCollection 的类的代码示例都使用这种代码。
  • else 是将执行引导到TPersistent.Assign,它调用Source.AssignTo(Self) 并为Source 实例提供实现分配操作的机会。这为一个类提供了一种机制,实际上,在另一个类中实现Assign 而无需修改另一个类。如果您只是阅读源代码,我相信您可以理解这一点。你明白inherited 是什么意思。
  • 让我换个方式问这个问题。当我从另一个组件派生一个组件时,第二种编码形式不是最安全的吗?我知道我在派生组件中添加了哪些属性。我可以在不查看基础来源的情况下编写代码,这对我来说似乎是多余的事情。那为什么不像构造函数那样形成一个标准,每个人都先调用继承,然后设置新的属性值?
  • 但是让我们弄清楚问题中的两个代码块。他们都是正确的。它们因阶级的不同遗产而有所不同。这里没有一种适合所有解决方案的尺寸。如果您花时间理解为什么这两个Assign 实现不同,通过阅读源代码,您将能够在编写自己的代码时做出正确的决定。

标签: delphi


【解决方案1】:

最后我开始解决这个问题。实际上,当我在 TCollection 上实现排序时,它没有工作,然后我终于能够看到哪里出了问题以及它应该如何工作。我必须查看旧 Delphi XE4 版本中的源代码来确认我的发现。

以下是编写重写的分配代码时要考虑的要点:

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

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

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

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

【讨论】:

    猜你喜欢
    • 2013-03-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-06
    • 2019-05-04
    • 2020-07-22
    相关资源
    最近更新 更多