C#深拷贝与浅拷贝

来源:互联网 发布:啄木鸟优化xp程序 编辑:程序博客网 时间:2024/06/08 04:23
<p><strong>MemberwiseClone</strong> 方法创建一个浅表副本,具体来说就是创建一个新对象,然后将当前对象的非静态字段复制到该新对象。如果字段是值类型的,则对该字段执行逐位复制。如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其复本引用同一对象。 </p><p>为了实现深度复制,我们就必须遍历有相互引用的对象构成的图,并需要处理其中的循环引用结构。这无疑是十分复杂的。幸好借助.Net的序列化和反序列化机制,可以十分简单的深度Clone一个对象。原理很简单,首先将对象序列化到内存流中,此时对象和对象引用的所用对象的状态都被保存到内存中。.Net的序列化机制会自动处理循环引用的情况。然后将内存流中的状态信息反序列化到一个新的对象中。这样一个对象的深度复制就完成了。在原型设计模式中CLONE技术非常关键。 </p><p>下面的代码就是演示这个问题:<pre><pre class="csharp" name="code">using System;using System.IO;using System.Runtime.Serialization.Formatters.Binary;namespace CloneDemo{    [Serializable]    class DemoClass    {        public int i = 0;        public int[] iArr = { 1, 2, 3 };        public DemoClass Clone1() //浅CLONE        {            return this.MemberwiseClone() as DemoClass;        }        public DemoClass Clone2() //深clone        {            MemoryStream stream = new MemoryStream();            BinaryFormatter formatter = new BinaryFormatter();            formatter.Serialize(stream, this);            stream.Position = 0;            return formatter.Deserialize(stream) as DemoClass;        }    }    class Program    {        static void Main(string[] args)        {            DemoClass a = new DemoClass();            a.i = 10;            a.iArr = new int[] { 8, 9, 10 };            DemoClass b = a.Clone1();            DemoClass c = a.Clone2();            // 更改 a 对象的iArr[0], 导致 b 对象的iArr[0] 也发生了变化 而 c不会变化              a.iArr[0] = 88;            Console.WriteLine("MemberwiseClone");            Console.WriteLine(b.i);            foreach (var item in b.iArr)            {                Console.WriteLine(item);            }            Console.WriteLine("Clone2");            Console.WriteLine(c.i);            foreach (var item in c.iArr)            {                Console.WriteLine(item);            }            Console.ReadLine();        }    }}

另外一个例子是针对数组,C#中的数组是引用型的变量,我们通过数组来进行

</pre><p><strong>浅拷贝:</strong> </p><pre class="csharp" name="code">    using System;    class ShallowCopy : ICloneable    {        public int[] v = { 1, 2, 3 };        public Object Clone()        {            return this.MemberwiseClone();        }        public void Display()        {            foreach (int i in v)                Console.Write(i + ", ");            Console.WriteLine();        }    }    class Client    {        public static void Main()        {            ShallowCopy sc1 = new ShallowCopy();            ShallowCopy sc2 = (ShallowCopy)sc1.Clone();            sc1.v[0] = 9;            sc1.Display();            sc2.Display();        }    }


ShallowCopy对象实现了一个浅拷贝,因此当对sc1进行克隆时,其字段v并没有克隆,这导致sc1与sc2的字段v都指向了同一个v,因此,当修改了sc1的v[0]后,sc2的v[0]也发生了变化。

深拷贝:

<pre class="csharp" name="code">    using System;    class DeepCopy : ICloneable    {        public int[] v = { 1, 2, 3 };        // 默认构造函数        public DeepCopy()        {        }        // 供Clone方法调用的私有构造函数        private DeepCopy(int[] v)        {            this.v = (int[])v.Clone();        }        public Object Clone()        {            // 构造一个新的DeepCopy对象,构造参数为            // 原有对象中使用的 v             return new DeepCopy(this.v);        }        public void Display()        {            foreach (int i in v)                Console.Write(i + ", ");            Console.WriteLine();        }    }    class Client    {        public static void Main()        {            DeepCopy dc1 = new DeepCopy();            DeepCopy dc2 = (DeepCopy)dc1.Clone();            dc1.v[0] = 9;            dc1.Display();            dc2.Display();        }    } 

这次在克隆的时候,不但克隆对象本身,连里面的数组字段一并克隆。因此,最终打印出来的dc1与dc2不同。

当然我们也可以建个深拷贝的帮助类:

public static class ObjectCopier{    /// <summary>    /// Perform a deep Copy of the object.    /// </summary>    /// <typeparam name="T">The type of object being copied.</typeparam>    /// <param name="source">The object instance to copy.</param>    /// <returns>The copied object.</returns>    public static T Clone<T>(T source)    {        if (!typeof(T).IsSerializable)        {            throw new ArgumentException("The type must be serializable.", "source");        }        // Don't serialize a null object, simply return the default for that object        if (Object.ReferenceEquals(source, null))        {            return default(T);        }        IFormatter formatter = new BinaryFormatter();        Stream stream = new MemoryStream();        using (stream)        {            formatter.Serialize(stream, source);            stream.Seek(0, SeekOrigin.Begin);            return (T)formatter.Deserialize(stream);        }    }}    


 

0 0