【问题标题】:Why is cloning (in .NET) so difficult?为什么克隆(在 .NET 中)如此困难?
【发布时间】:2010-11-21 11:35:03
【问题描述】:

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

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

【问题讨论】:

    标签: .net theory cloning


    【解决方案1】:

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

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

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

    【讨论】:

    • 不可变类型充满了win。另外,请注意 MemberwiseClone 是受保护的,否则您可能会严重地将语义分解为核心 ECMA-335 规范,例如两个 System.Type 对象在它们引用内存中的相同对象时相等。
    • 同时写了类似的东西,用淡漠的话。我认为遗憾的是 .NET 在类型(类/结构)上定义了“by-ref”和“by-val”,而不是包含它的字段/变量。这实际上是您进行克隆...和其他事情所需的信息。
    • @Stefan:这个主题是我本学期研究项目的主题。事实证明,它并没有导致语言表达能力的根本问题,但背后的推理并不是我最初的想法。
    • 如果所有对象都实现DeeplyCloneable 接口(粗略的想法),难道不能有一个方法DeepClone 进行深度克隆吗?显然这会影响到许多类,而且克隆可能不是那么受欢迎的活动。
    • @yar:是的,可能有 DeepClone 方法。就是没有:)
    【解决方案2】:

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

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

    【讨论】:

    • 唯一的问题是它是一个受保护的方法,所以除非类设计者决定公开它,否则你就不走运了。
    【解决方案3】:

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

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

    【讨论】:

      【解决方案4】:

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

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

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

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

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

      【讨论】:

      • @Pavel:我在回答之前对 Jon 的评论中暗示了这个问题。您的答案当然是更完整的解释。 :)
      【解决方案5】:

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

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

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

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

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

      显然,您不想在两个Orders 之间共享OrderLinesList。事实上,您可能还想克隆每个OrderLine

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

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

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

      【讨论】:

        猜你喜欢
        • 2019-01-09
        • 2018-11-21
        • 1970-01-01
        • 1970-01-01
        • 2023-01-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-04-25
        相关资源
        最近更新 更多