20

过去我需要克隆对象,结果发现它们没有实现Clone()方法,迫使我手动完成(创建一个新实例并将所有属性从原始实例复制到新实例)

为什么克隆不像复制分配对象的内存块那样容易,因此Clone在类中拥有方法object,让 .NET 中的所有类都继承它?

4

5 回答 5

25

因为这不会执行深度克隆,而这通常是克隆真正需要的。想象一下,您有一个对数组或列表的引用……只需复制对象占用的内存即可克隆该引用。对数组的任何更改都将通过克隆以及原始对象可见 - 因此两个对象仍然连接,这违反了正常的克隆点。

如果您想完全实现该功能,这很容易 - 这就是Object.MemberwiseClone()目的。大多数时候,如果克隆一个对象是有意义NetworkStream的(克隆是什么意思?)克隆每个属性也是有意义的……除非它已经引用了一个不可变的值,等等。换句话说,这是一个自然是难题,这就是为什么大多数类型不支持克隆的原因。

如果您尽可能坚持使用不可变类型,那么这并不是什么大问题......诚然,这会使其他事情变得更难,但在许多情况下它可能非常强大。

于 2009-08-20T21:07:05.850 回答
8

其他人已经解释过了MemberwiseClone,但没有人解释为什么它受到保护。我会试着给出理由。

这里的问题是MemberwiseClone只是盲目地复制状态。在许多情况下,这是不可取的。例如,该对象可能有一个私有字段,它是对 a 的引用List。一个浅拷贝,比如什么MemberwiseClone,会导致新的对象指向同一个列表——并且这个类很可能被编写成不期望与其他任何人共享该列表。

或者一个对象可以有某种 ID 字段,在构造函数中生成 - 同样,当你克隆它时,你会得到两个具有相同 ID 的对象,这可能会导致假设 ID 是唯一的方法中的各种奇怪的失败。

或者说你有一个对象可以打开一个套接字或一个文件流,并存储一个对它的引用。MemberwiseClone只会复制引用 - 你可以想象两个对象试图交叉调用同一个流不会很好地结束。

简而言之,“克隆”不是针对任意对象的定义明确的操作。在 C++ 中默认为所有类提供memberwise 的事实operator=更令人讨厌,因为人们经常忘记它的存在,并且不要为复制没有意义或危险的类禁用它(和有很多这样的课程)。

于 2009-08-20T21:14:29.783 回答
4

有(至少)两种克隆。大多数参考文献都在谈论浅层深层克隆,但实际上两者之间存在阴影。

关键问题是“应该复制多少”和“应该分享多少”之间的张力。

考虑一个Order对象,包含对 a Customer、 anAddress和 a Listof的引用OrderLines

如果你想要Clone()一个Order,涉及什么?

只是复制内存块”会给你一个新的Order,但一个共享的CustomerAddress和相同ListOrderLines。(请记住,对象成员是通过引用存储的,因此当您复制内存块时,您最终会得到对同一对象的两个引用)。

显然,您不想在两个s之间共享Listof 。事实上,您可能也想克隆每个。OrderLinesOrderOrderLine

如果您使用的是通用Clone()方法,该方法如何知道哪些成员应该递归克隆,哪些不应该?

一般来说,这是一个棘手的问题——这就是为什么要由单个对象来为它们的情况实现适当的语义

最后一点:即使我确实创建了Clone()对象的能力,我也不倾向于创建Clone()方法,而是更喜欢复制构造函数——一个接受另一个对象作为基础的构造函数。如果找不到Clone(),请寻找。

于 2009-08-20T21:15:21.403 回答
2

有一个像Object.MemberwiseClone() 这样的东西,它可以按照您的描述进行操作。它正在制作对象的浅表副本。

它不会自动进行深度克隆……您需要手动在所有成员对象上调用克隆,等等。

于 2009-08-20T21:08:26.080 回答
0

您必须在您的类中显式实现ICloneable接口。

但是,正如文档所述,MemberwiseClone中的克隆机制不区分浅拷贝和深拷贝。

于 2009-08-20T21:10:05.203 回答