克隆整个对象图

6

当使用以下代码序列化对象时:

public object Clone()
{
    var serializer = new DataContractSerializer(GetType());
    using (var ms = new System.IO.MemoryStream())
    {
        serializer.WriteObject(ms, this);
        ms.Position = 0;
        return serializer.ReadObject(ms);
    }
}

我注意到它不会复制关联关系。有什么方法可以实现这个功能吗?


我注意到了同样的问题,它可能会深入几个层级,但大多数情况下你最终得到的只是一个部分和无用的图表。 - leppie
@leppie - 可以做到,不过 ;-p - Marc Gravell
4个回答

16

只需使用接受 preserveObjectReferences 参数的构造函数重载,并将其设置为 true 即可:

using System;
using System.Runtime.Serialization;

static class Program
{
    public static T Clone<T>(T obj) where T : class
    {
        var serializer = new DataContractSerializer(typeof(T), null, int.MaxValue, false, true, null);
        using (var ms = new System.IO.MemoryStream())
        {
            serializer.WriteObject(ms, obj);
            ms.Position = 0;
            return (T)serializer.ReadObject(ms);
        }
    }
    static void Main()
    {
        Foo foo = new Foo();
        Bar bar = new Bar();
        foo.Bar = bar;
        bar.Foo = foo; // nice cyclic graph

        Foo clone = Clone(foo);
        Console.WriteLine(foo != clone); //true - new object
        Console.WriteLine(clone.Bar.Foo == clone); // true; copied graph

    }
}
[DataContract]
class Foo
{
    [DataMember]
    public Bar Bar { get; set; }
}
[DataContract]
class Bar
{
    [DataMember]
    public Foo Foo { get; set; }
}

哎呀!我应该再找一些提示的! :) - leppie
这应该是我能想到的最好的方式...谢谢Marc,但我在Object.Object2.Object3方面有问题,这可能会影响序列化器吗? - George Taskos

1

要么在你的类上注释[DataContract],要么在DatacontractSerializer的构造函数中添加你的子类型。

var knownTypes = new List<Type> {typeof(Class1), typeof(Class2), ..etc..};
var serializer = new DataContractSerializer(GetType(), knownTypes);

1

要执行深度克隆,您可以考虑使用二进制序列化器:

public static object CloneObject(object obj)
{
    using (var memStream = new MemoryStream())
    {
        var binaryFormatter = new BinaryFormatter(
             null, 
             new StreamingContext(StreamingContextStates.Clone));
        binaryFormatter.Serialize(memStream, obj);
        memStream.Seek(0, SeekOrigin.Begin);
        return binaryFormatter.Deserialize(memStream);
    }
}

0

在序列化/反序列化过程中,您需要使用二进制序列化器来保留对象的标识。


是的,但我使用SqlMetal来创建类和触发器/序列化:单向。抱歉给您带来不便。 - George Taskos
我不明白你的评论与我的答案有什么关系...?看看Darin Dimitrov提供的代码示例。 - Seb
1
抱歉@Seb,但那不正确。DataContractSerializer 可以 复制适当的图形,并且不是二进制序列化程序。相反,所有二进制序列化程序都不能必然复制图形也不是真实的。图形支持和二进制输出是正交概念。 - Marc Gravell
@Marc,我认为XmlSerialization永远无法保留对象标识。我错了吗?如果是这样,我该怎么做?[我习惯于使用.NET 2.0,但我是3.0的新手,还没有在3.5上工作过,请温柔一点;-)] - Seb

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