4

我想知道,对于原型模式的简单实现,Delphi 的 RTTI 中是否有任何与 C# 中的 MemberwiseClone 相同的功能。我看到了这种模式的一些 Delphi 实现,其中正在创建一个新对象 (TMyObject.Create),它的属性分配有来自原型对象的值。我可能是错的,但是如果我们以相同的基本方式创建对象,我看不到该模式的好处。

谢谢你。

4

5 回答 5

9

Object.MemberwiseClone 方法遵循一些非常简单的规则并利用 .NET 垃圾收集器的工作原理制作对象的浅表副本。

  • 引用只是简单地复制。这包括字符串和对任何object.
  • 值类型是位复制的(生成相同的克隆)。

关于值类型的部分可以很容易地用 Delphi 复制。用 Delphi 复制引用类型的行为虽然在技术上很容易,但不会提供预期的结果:.free它创建的对象需要 Delphi 代码,并且它使用owner-owned范式来确保发生这种情况。通常的模式是从析构函数中释放所有者对象创建的对象。如果您制作对象的浅拷贝,则会导致失败。这是一个例子:

  • 对象 A 拥有对对象 B 的引用。
  • 我们将对象 C 创建为对象 A 的浅表副本。对象 C 现在包含对对象 B 的引用。
  • 我们释放对象 A:A.Free;
  • 我们释放对象 B:B.Free;- 这会自动调用B.Free,但不幸的是,当我们释放 A 时,B 已经被释放了!

正如大卫建议的那样,我们可以尝试 a deep-copy,但这会带来一些同样困难的问题:

  • 并非所有对象都应该被复制,例如因为它们封装了对现实世界资源的引用(例如:TFileStream)。
  • 其他一些对象不能被深度复制,因为它们本质上是单例。并且没有通用的说法“这个对象是一个单例,做一个普通的引用副本,不要做一个深拷贝”。示例:我们复制Application吗?
  • 如果你做一个深拷贝,你可能有循环引用,你需要照顾这些。这不是微不足道的,并且您从集合中的项目开始复制,您可能会发现自己回到集合的父级,即:不完全是预期的结果。
  • 不加选择的深度应对可能会占用意外的内存量并导致意外的内存泄漏。再次考虑集合 -> 项目 -> 复制项目示例,您最终得到“项目”的副本,但由于意外的反向链接,整个 COLLECTION 被复制。

综上所述,我们只能得出一个结论:我们不能有一个通用的,Delphi 等价于MemberwiseClone. 对于具有不复杂交互的简单对象,我们可以部分相似,但这并不那么吸引人!

于 2011-03-30T11:12:24.713 回答
6

没有任何内置功能可以为您执行深度克隆。我确信您可以基于新的 RTTI 编写深度克隆,但我希望这将是一项不平凡的工作。

如果您正在处理足够简单的类型,它会工作得很好,但您很容易遇到严重的挑战。例如,在我的脑海中:

  • 需要按特定顺序创建某些对象组。
  • 不应克隆类的某些成员,例如引用计数。你如何识别那些有 RTTI 的人?
  • 你如何处理单身人士?
  • 需要设置的任何外部引用呢?假设您克隆了一个通常由工厂创建的对象。如果该工厂拥有对其创建的对象的引用,那么在其背后可能会破坏您的设计。

您可以通过定义一个将 RTTI 用于简单类型的基本方法来实现您的原型模式Clone(),然后您必须为任何更复杂的类型覆盖它。不过就个人而言,我会继承TPersistentClone()基于Assign.

于 2011-03-30T10:26:54.687 回答
4

有一种方法可以在 Delphi 中执行对象的深拷贝(克隆)。它适用于最新版本的 Delphi(2010 及更高版本)。请参阅下面的代码...实际上非常简单,您不需要外部库。您可以在这里找到更多信息:http ://www.yanniel.info/2012/02/deep-copy-clone-object-delphi.html

function DeepCopy(aValue: TObject): TObject;
var
  MarshalObj: TJSONMarshal;
  UnMarshalObj: TJSONUnMarshal;
  JSONValue: TJSONValue;
begin
  Result:= nil;
  MarshalObj := TJSONMarshal.Create;
  UnMarshalObj := TJSONUnMarshal.Create;
  try
    JSONValue := MarshalObj.Marshal(aValue);
    try
      if Assigned(JSONValue) then
        Result:= UnMarshalObj.Unmarshal(JSONValue);
    finally
      JSONValue.Free;
    end;
  finally
    MarshalObj.Free;
    UnMarshalObj.Free;
  end;
end;
于 2012-02-08T16:24:48.720 回答
1

我认为您正在寻找类似的东西:http ://code.google.com/p/delphilhlplib/source/browse/trunk/Library/src/Extensions/DeHL.Cloning.pas

它仅适用于 D2010 及更高版本(需要扩展 RTTI)。

于 2011-03-30T10:55:44.640 回答
1

不久前,我发布了一个可能有用的通用组件克隆MemberWiseClone答案,尽管它不等同于. 我相信它在 Delphi 中可以追溯到 D5,而且我确信它在 D2007 中也可以使用。

于 2011-03-30T17:45:53.483 回答