复制Delphi对象的正确方法

发布于 2024-09-29 16:53:41 字数 1482 浏览 3 评论 0原文

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

示例 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 或“为什么我想要单行操作”

相比,使用量非常大,我同意在大多数情况下分配是一种合理的方法。通过简单地使用赋值在内部实现“复制构造函数”甚至是合理的。

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

IOW,我更喜欢

Send(TMyObject.Create(obj));

Send(obj.Clone);

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

What are pros and cons of duplication an object instance with constructor or instance function?

Example 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;

Example 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;

One major difference immediately springs to mind - in the latter case the Create constructor would have to be virtual so that a class hierarchy supporting Clone could be built basing on the TMyObject.

Assume that this is not a problem - that TMyObject and everything based on it is entirely under my control. What is your preferred way of doing copy constructor in Delphi? Which version do you find more readable? When would you use former or latter approach? Discuss. :)

EDIT:
My main concern with the first example is that the usage is very heavy compared to the second approach, i.e.

newObj := TMyObject.Create(oldObj)

vs.

newObj := oldObj.Clone;

EDIT2 or "Why I want single-line operation"

I agree that Assign is a reasonable approach in most cases. It's even reasonable to implement 'copy constructor' internally by simply using assign.

I'm usually creating such copies when multithreading and passing objects through the message queue. If object creation is fast, I usually pass a copy of the original object because that really simplifies the issues of object ownership.

IOW, I prefer to write

Send(TMyObject.Create(obj));

or

Send(obj.Clone);

to

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

旧伤慢歌 2024-10-06 16:53:41

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

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

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 默认参数并且它有效,所以我添加了它。

Ps (2024) 很高兴看到类助手是否也可以用于非根类实现这一点。

The first adds information about which object to want to create, the second not. This can be used to instantiate e.g. a descendant or an ancestor of a class

The Delphi way (TPersistent) separates creation and cloning:

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

and has this same property that you explicitly choose the class to instantiate. But you don't need two constructors, one for normal use, and one where you want to clone.

edit due to oneline requirement
You can mix it of course using Delphi metaclasses (untested)

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 

For the rest, just implement your assign() operators, and you can mix multiple ways.

edit2

I replaced the code above with code tested in D2009. There are some dependencies of the types that might have confused you, hope it is clearer this way. Of course you'll have to study the assign mechanism. I also tested the metaclass=nil default parameter and it works, so I added it.

P.s. (2024) it would be nice to see if class helpers can be used to implement this also for non root classes.

記柔刀 2024-10-06 16:53:41

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

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

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

I don't think there is a correct way it just depend on personal style. (And as Marco pointed out, there are more ways.)

  • The constructor way is short but it violates the principle that the constructor must only construct the object. Which is possibly not a problem.
  • The clone way is short although you need to provide a call for each class.
  • The assign way is more Delphi like. It separates creation and initialization which is good because we like the one method one function concept that makes code better to maintain.

And if you implement Assign using streams, you have only one place to worry about which fields need to be available.

悲凉≈ 2024-10-06 16:53:41

我喜欢克隆风格——但仅限于Java(或任何其他GC语言)。我在 Delphi 中使用过它几次,但大多数时候我还是使用 Create 和 Assign ,因为谁负责对象的销毁更加清晰。

I like the clone style - but only in Java (or any other GC language). I used it some times in Delphi, but mostly I stay with Create and Assign, because it is much clearer who is responsible for the destruction of the object.

埋情葬爱 2024-10-06 16:53:41

我使用第二种方法,即带有 Clone 函数的方法,即使对于复杂的类,它也很有魅力。我发现它更具可读性和防错性。

I use the second method, the one with the Clone function, and it works like a charm, even with complex classes. I find it more readable and error proof.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文