C#中的对象克隆

4
考虑这个相关的问题:在C#中深度克隆对象
这真的是克隆对象的最佳方法吗?(序列化/反序列化)。对我来说,序列化似乎有点昂贵。
我的想法是创建第二个构造函数并仅分配所有变量。这种方法会更快吗?
class Test
{

public Test(Test clone)
{
// clone ....
}
}

请查看此文章:http://www.codeproject.com/Articles/28952/Shallow-Copy-vs-Deep-Copy-in-NET - Perpetualcoder
链接的答案开始提到了 ICloneable,并且只在无法使用它时提到了序列化。你是不是跳过了第一句话? - Ben Voigt
你问错了问题。问题应该是“哪一条途径会导致更易维护的代码,而且这条途径是否会在我的应用程序中引入性能瓶颈”? - Ed S.
6个回答

6

这取决于你想要克隆什么以及为什么要克隆。 如果浅度克隆可以接受,那么:

private static T CloneShallow<T>(T i) {
    return
        (T)i.GetType()
            .GetMethod(
                "MemberwiseClone",
                BindingFlags.Instance | BindingFlags.NonPublic
            ).Invoke(i, null);
}

这是一种最快的方式,可以克隆任何类型的简单记录。

如果您需要完全复制,但不着急的话,请使用以下方法:

private static T CloneFull<T>(T i) {
    if (Object.ReferenceEquals(i, null)) return default(T);
    var x = new XmlSerializer(i.GetType());
    using (var m = new MemoryStream()) {
        x.Serialize(m, i);
        m.Seek(0, SeekOrigin.Begin);
        return (T)x.Deserialize(m);
    }
}

第二个方法比第一个方法慢大约70倍。如果您需要完整的副本并且速度很重要,请考虑使用protobuf-net作为序列化器。有很多答案涉及修改要克隆的对象,但通常您只需要克隆一个简单的记录,并且不想对其实现任何内容。如果您想知道CloneFull()有多慢-在我的电脑上(i5 661 @ 3.33GHz)非常简单的记录需要360个时钟周期,即36ns。在大多数情况下,这已经足够快了 :)

2

您所提到的问题涉及支持序列化的任何内容的通用克隆。如果您只想为您特定的对象实现克隆,请实现ICloneable

class Test : ICloneable {
    public object Clone() {
        // clone ....
    }
}

1
值得注意的是,ICloneable不建议在API中使用 - Rowland Shaw
@RowlandShaw 这是真的,使用 ICloneable 确实不被鼓励,因为该接口并未指定克隆的类型。然而,OP 建议使用“复制构造函数”-样式的克隆,这通常根本不算是克隆。比较这两个想法时,使用 ICloneable 更可取。 - Sergey Kalinichenko
只要您能保证所有子对象都支持克隆到相同的合同,就可以使用克隆方法。我必须承认,我倾向于实现一个复制构造函数,并让“Clone”调用它;在继承层次结构中处理起来也更容易。走序列化/反序列化路线可能会导致非常糟糕的性能问题。 - Rowland Shaw
Clone() 需要调用构造函数,而且没有添加任何构造函数不能完成的操作。最好避免 Clone() 的不必要开销,并为克隆专门创建一个特殊的构造函数,例如 public MyClass(MyClass original): this(/*在此处传递所有属性*/) {} - Peter Huber

2
关键词在接受的答案中。

好处是当对象变得太复杂时,您不必担心克隆所有内容。

序列化方法适用于任何复杂度的对象。虽然它可能对于较小、更简单的对象来说过于繁琐,但它是一种可扩展的解决方案。

如果您知道您的对象不会改变,并且您只想克隆简单的对象,则您的方法是完全可接受的。尽管实现ICloneable接口是首选解决方案。


1
序列化特别适用于存储对象的表示形式,以便稍后/其他地方检索。如果要克隆对象,则应实现ICloneable接口。

1
IClonable的MSDN页面提到了Serialization作为一种可能的实现方法。 - H H
2
值得注意的是,ICloneable不建议在API中使用 - Rowland Shaw

1

ICloneable

引用:

支持克隆,即创建一个具有与现有实例相同值的类的新实例。 ICloneable接口包含一个成员Clone,旨在支持超出MemberwiseClone提供的克隆。有关克隆、深度与浅复制以及示例的更多信息,请参阅Object.MemberwiseClone方法。


2
值得注意的是,ICloneable不建议在API中使用 - Rowland Shaw
@Rowland Shaw 为什么? - Subin Jacob
因为接口没有明确表明消费者应该期望浅拷贝还是深拷贝,这是一种实现选择。这并不被认为是有帮助的。 - Pete Stensønes

1
如果你想使用自定义类进行复制,最好的方式是实现 ICloneable 接口。这样你的类就可以提供一个标准化的克隆自己的方式。
有时候使用“拷贝构造函数”可能更好,但通常只有在你可以确定它很简单,并且 ICloneable 不能满足你的需求时才会使用。

4
值得注意的是,ICloneable 不建议在 API 中使用。 - Rowland Shaw

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接