在实施ICloneable时,我应该提供深度克隆吗?

时间:2008-09-30 02:17:13

标签: .net oop

我不清楚MSDN documentation我是否应该在实施ICloneable时提供深度或浅层克隆。什么是首选方案?

3 个答案:

答案 0 :(得分:15)

简短回答:是的。

长答案:不要使用ICloneable。这是因为.Clone未被定义为浅层或深层克隆。您应该实现自己的IClone接口,并描述克隆应该如何工作。

答案 1 :(得分:1)

默认情况下克隆很深,这就是命名约定 出于性能原因,复制构造函数可以是浅层的。

编辑:这个命名约定超出了界限,对于.Net,Java,C ++,Javascript等也是如此......实际的来源超出了我的知识,但它是标准的面向对象的词典的一部分,就像对象一样,和课程。因此,MSDN没有指定实现,因为它是由单词本身给出的(当然很多OO语言的新手都不知道这一点,他们应该指定它,但是反过来他们的文档仍然非常节俭)

答案 2 :(得分:1)

考虑到对象的定义方式,不应该有任何关于“深度克隆”与“浅层克隆”的问题。如果一个对象封装了事物的身份,那么该对象的一个​​克隆应该封装相同事物的身份。如果一个对象封装了可变对象的值,那么副本应该封装具有相同值的分离的可变对象。

不幸的是,在类型系统中,.NET或Java都不包括引用是否包含身份,可变值,两者或两者都不包含。相反,它们只使用单个引用类型并且表明拥有引用的唯一副本的代码,或者拥有对容纳该引用的唯一副本的容器的唯一引用,可以使用该引用来封装值或状态。对于单个对象来说,这种想法可能是可以容忍的,但是当涉及到复制和相等测试操作等事情时,这会带来真正的问题。

如果一个类有一个字段Foo,它封装了List<Bar>的状态,用于封装其中的对象的身份,并且可能在将来封装不同对象的身份,那么Foo应该包含对标识相同对象的新列表的引用。如果List<Bar>用于封装对象的可变状态,则克隆应该具有对新列表的引用,该列表标识具有相同状态的新对象。

如果对象包含单独的“等效”和“等于”方法,每个方法都有哈希码,并且如果每个堆对象类型都有引用类型,表示为封装标识,可变状态,两者或两者,那么99%可以自动处理相等测试和克隆方法。如果封装了身份或可变状态的所有组件都是等效的(不仅仅是相等的),那么两个聚合是相等的,并且那些封装它们的组件至少是相等的;只有当所有相应的组件都是等价的时候,两个聚合才是等价的[这通常意味着引用相等,但并不总是如此]。复制聚合需要制作封装可变状态的每个成分的分离副本,复制对封装身份的每个成分的引用,并对那些既不封装的内容执行上述任一操作;不能简单地克隆包含可变状态和同一性的成分的聚合。

有一些棘手的案例表明克隆,等同和等价的规则无法正确处理,但是如果有一个约定来区分List<IdentityOfFoo>List<MutableStateOfFoo>,并支持“等效”和“等于”测试,99%的对象可以自动生成Clone,Equals,Equivalent,EqualityHash和EquivalenceHash并正常工作。