过去我需要克隆对象,结果发现它们没有实现Clone()
方法,迫使我手动完成(创建一个新实例并将所有属性从原始实例复制到新实例)
为什么克隆不像复制分配对象的内存块那样容易,因此Clone
在类中拥有方法object
,让 .NET 中的所有类都继承它?
因为这不会执行深度克隆,而这通常是克隆真正需要的。想象一下,您有一个对数组或列表的引用……只需复制对象占用的内存即可克隆该引用。对数组的任何更改都将通过克隆以及原始对象可见 - 因此两个对象仍然连接,这违反了正常的克隆点。
如果您想完全实现该功能,这很容易 - 这就是Object.MemberwiseClone()
目的。大多数时候,如果克隆一个对象是有意义NetworkStream
的(克隆是什么意思?)克隆每个属性也是有意义的……除非它已经引用了一个不可变的值,等等。换句话说,这是一个自然是难题,这就是为什么大多数类型不支持克隆的原因。
如果您尽可能坚持使用不可变类型,那么这并不是什么大问题......诚然,这会使其他事情变得更难,但在许多情况下它可能非常强大。
其他人已经解释过了MemberwiseClone
,但没有人解释为什么它受到保护。我会试着给出理由。
这里的问题是MemberwiseClone
只是盲目地复制状态。在许多情况下,这是不可取的。例如,该对象可能有一个私有字段,它是对 a 的引用List
。一个浅拷贝,比如什么MemberwiseClone
,会导致新的对象指向同一个列表——并且这个类很可能被编写成不期望与其他任何人共享该列表。
或者一个对象可以有某种 ID 字段,在构造函数中生成 - 同样,当你克隆它时,你会得到两个具有相同 ID 的对象,这可能会导致假设 ID 是唯一的方法中的各种奇怪的失败。
或者说你有一个对象可以打开一个套接字或一个文件流,并存储一个对它的引用。MemberwiseClone
只会复制引用 - 你可以想象两个对象试图交叉调用同一个流不会很好地结束。
简而言之,“克隆”不是针对任意对象的定义明确的操作。在 C++ 中默认为所有类提供memberwise 的事实operator=
更令人讨厌,因为人们经常忘记它的存在,并且不要为复制没有意义或危险的类禁用它(和有很多这样的课程)。
有(至少)两种克隆。大多数参考文献都在谈论浅层和深层克隆,但实际上两者之间存在阴影。
关键问题是“应该复制多少”和“应该分享多少”之间的张力。
考虑一个Order
对象,包含对 a Customer
、 anAddress
和 a List
of的引用OrderLines
。
如果你想要Clone()
一个Order
,涉及什么?
“只是复制内存块”会给你一个新的Order
,但一个共享的Customer
,Address
和相同List
的OrderLines
。(请记住,对象成员是通过引用存储的,因此当您复制内存块时,您最终会得到对同一对象的两个引用)。
显然,您不想在两个s之间共享List
of 。事实上,您可能也想克隆每个。OrderLines
Order
OrderLine
如果您使用的是通用Clone()
方法,该方法如何知道哪些成员应该递归克隆,哪些不应该?
一般来说,这是一个棘手的问题——这就是为什么要由单个对象来为它们的情况实现适当的语义。
最后一点:即使我确实创建了Clone()
对象的能力,我也不倾向于创建Clone()
方法,而是更喜欢复制构造函数——一个接受另一个对象作为基础的构造函数。如果找不到Clone()
,请寻找。
有一个像Object.MemberwiseClone() 这样的东西,它可以按照您的描述进行操作。它正在制作对象的浅表副本。
它不会自动进行深度克隆……您需要手动在所有成员对象上调用克隆,等等。
您必须在您的类中显式实现ICloneable接口。
但是,正如文档所述,MemberwiseClone中的克隆机制不区分浅拷贝和深拷贝。