为什么克隆(在.NET中)如此困难?

时间:2009-08-20 21:04:55

标签: .net theory cloning

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

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

5 个答案:

答案 0 :(得分:24)

因为那不会执行深度克隆,通常克隆确实需要。想象一下,你有一个数组或列表的引用......只需复制你的对象所占用的内存就会克隆引用。通过克隆和原始对象可以看到对数组的任何更改 - 因此两个对象仍然连接,这违反了正常的克隆点。

如果您想要实现该功能,那很容易 - 这就是Object.MemberwiseClone()的用途。大多数情况下,如果它甚至使有意义克隆一个对象(克隆NetworkStream是什么意思?),克隆每个属性是有意义的......除非它引用一个不可变的换句话说,这是一个自然难以解决的问题,这就是大多数类型不支持克隆的原因。

如果你坚持使用不可变的类型,那就不是一个问题......这使得其他事情变得更加难以理解,但在很多情况下它可能非常强大。

答案 1 :(得分:7)

其他人已经解释了MemberwiseClone,但没有人给出为什么受保护的解释。我会尽力给出理由。

这里的问题是MemberwiseClone只是盲目地复制了州。在许多情况下,这是不可取的。例如,对象可能具有私有字段,该字段是对List的引用。浅层副本(例如MemberwiseClone所做的那样)会导致新对象指向同一个列表 - 并且可能会编写类而不期望列表与其他任何人共享。

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

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

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

答案 2 :(得分:4)

有(至少)两种克隆。大多数参考文献都讨论了克隆,但实际上它们之间存在着色调。

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

考虑一个Order对象,其中包含对CustomerAddressList OrderLines的引用。

如果您想Clone() Order,有什么参与?

只是复制内存块”会为您提供一个新的Order,但会共享CustomerAddressList } OrderLines。 (请记住,对象成员是通过引用存储的,因此当您复制内存块时,最终会有两个对同一对象的引用)。

显然,您不希望在两个List之间共享OrderLines Order。实际上,您可能也希望克隆每个OrderLine

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

一般来说,这是一个棘手的问题 - 这就是为什么单个对象可以根据情况实现适当的语义

最后一点:即使我创建了Clone()个对象的功能,我也不倾向于创建Clone()方法,而是选择复制构造函数 - 一个接受另一个对象作为基础的构造函数。如果找不到Clone(),请查找。

答案 3 :(得分:2)

Object.MemberwiseClone(),之类的东西可以完成你描述的内容。它正在制作一个浅物体的副本。

它不会自动执行深度克隆...您需要手动调用所有成员对象上的克隆等。

答案 4 :(得分:0)

您必须在类中明确实现ICloneable接口。

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