【问题标题】:Correct way to duplicate Delphi object复制Delphi对象的正确方法
【发布时间】:2011-05-01 19:16:24
【问题描述】:

用构造函数或实例函数复制对象实例的优缺点是什么?

示例 A:

type
  TMyObject = class
  strict private
    FField: integer; 
  public
    constructor Create(srcObj: TMyObject); overload; 
    //alternatively:
    //constructor CreateFrom(srcObj: TMyObject);
    property Field: integer read FField;
  end;

constructor TMyObject.Create(srcObj: TMyObject);
begin
  inherited Create;
  FField := srcObj.Field;
end;

示例 B:

type
  TMyObject = class
  strict private
    FField: integer; 
  public
    function Clone: TMyObject;
    property Field: integer read FField;
  end;

function TMyObject.Clone: TMyObject;
begin
  Result := TMyObject.Create;
  Result.FField := FField;
end;

一个主要区别立即浮现在脑海 - 在后一种情况下,Create 构造函数必须是虚拟的,以便可以基于 TMyObject 构建支持 Clone 的类层次结构。

假设这不是问题 - TMyObject 和基于它的一切都完全在我的控制之下。在 Delphi 中,您首选的复制构造函数是什么?你觉得哪个版本更具可读性?您什么时候使用前一种或后一种方法?讨论。 :)

编辑: 我对第一个示例的主要关注是与第二种方法相比,使用量非常大,即

newObj := TMyObject.Create(oldObj)

对比

newObj := oldObj.Clone;

EDIT2 或“我为什么要单行操作”

我同意在大多数情况下,Assign 是一种合理的方法。通过简单地使用assign在内部实现“复制构造函数”甚至是合理的。

我通常在多线程和通过消息队列传递对象时创建这样的副本。如果对象创建速度很快,我通常会传递原始对象的副本,因为这确实简化了对象所有权的问题。

IOW,我更喜欢写

Send(TMyObject.Create(obj));

Send(obj.Clone);

newObj := TMyObject.Create;
newObj.Assign(obj);
Send(newObj);

【问题讨论】:

    标签: delphi constructor copy-constructor


    【解决方案1】:

    第一个添加有关要创建的对象的信息,第二个不添加。这可以用于实例化例如一个类的后代或祖先

    Delphi 方式 (TPersistent) 将创建和克隆分开:

    dest := TSomeClass.Create; 
    dest.Assign(source);  
    

    并且具有您明确选择要实例化的类的相同属性。但是你不需要两个构造函数,一个用于正常使用,一个用于克隆。

    因单行需求而编辑 您当然可以使用 Delphi 元类(未经测试)混合它

    type
      TBaseSomeObject = class;
      TBaseObjectClass = class of TBaseSomeObject;
    
      TBaseSomeObject = class(TPersistent)
        function Clone(t: TBaseObjectClass = nil): TBaseSomeObject; virtual;
      end;
    
    ...
    
      function TBaseSomeObject.Clone(t: TBaseObjectClass = nil): TBaseSomeObject;
      begin
        if Assigned(t) then
          Result := t.Create
        else
          Result := TBaseObjectClass(Self.ClassType).Create;
        Result.Assign(Self);
      end;
    
    
     SendObject(obj.Clone); // full clone.
     SendObject(obj.Clone(TDescandantObject)); // Cloned into Descendant object 
    

    其余的,只需实现您的assign() 运算符,您可以混合多种方式。

    edit2

    我用 D2009 中测试的代码替换了上面的代码。类型的一些依赖项可能会让您感到困惑,希望这样更清楚。当然,您必须研究分配机制。我还测试了metaclass=nil默认参数,它可以工作,所以我添加了它。

    【讨论】:

    • 我会投票给你的答案,但这会破坏你的 5,555 分。 ;-) 恭喜!
    • 好吧,我确实想有一天达到 6666 :-)
    • 我无法让您的元类解决方案在 2007 或 XE 中工作。您能否更正您的示例以便编译?
    • 现在一切都清楚了。 (其实之前都清楚,但是我一次又一次愚蠢地误读了你的代码。)
    • 最大的问题当然是你必须建立一个自己的根类。
    【解决方案2】:

    我认为没有正确的方法,它只取决于个人风格。 (正如 Marco 指出的那样,还有更多方法。)

    • 构造方法很短,但是违反了构造方法只能构造对象的原则。这可能不是问题。
    • 虽然您需要为每个类提供调用,但克隆方式很短。
    • assign 方式更像Delphi。它将创建和初始化分开,这很好,因为我们喜欢使代码更易于维护的一种方法一种功能的概念。

    如果您使用流实现分配,您只需担心哪些字段需要可用。

    【讨论】:

      【解决方案3】:

      我喜欢 clone 风格 - 但仅限于 Java(或任何其他 GC 语言)。我在 Delphi 中使用过几次,但大多数情况下我还是使用 CreateAssign,因为更清楚谁负责销毁对象。

      【讨论】:

      • 我通常讨厌使用带参数的构造函数。当你开始让你的框架变得更复杂时,它开始在 中咬你
      • 我真的很喜欢强制传递所有必要对象的构造函数,比如Create(AOwner)。依赖注入框架知道两种风格,基于属性或构造函数的依赖注入 - 两者都有其优点(和缺点)。
      • @mjustin:我也是,但我通常不使用构造函数来创建副本。
      【解决方案4】:

      我使用第二种方法,即具有 Clone 功能的方法,它的工作原理非常棒,即使是复杂的类也是如此。我发现它更具可读性和防错性。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-02-12
        • 2017-11-01
        • 2013-02-08
        • 2018-10-18
        相关资源
        最近更新 更多