C#深度复制和浅度复制

2012-04-27 22:05:19|?次阅读|上传:wustguangh【已有?条评论】发表评论

关键词:C#|来源:唯设编程网

利用受保护的方法System.Object.MemberwiseClone()可以进行浅度复制,也就是说对于含有引用类型字段的对象来说,进行浅度复制意味着复制的对象和源对象存在这相同的引用。如果源对象的引用变量的值发生改变,那么被复制的对象的成员值也会发生变化。有很多适合这都不是我们希望看到的结果, 所以有必要对对象进行深度复制,这可以通过ICloneable接口的Clone方法实现,它通过创建新对象的方法进行深度复制。在比较复杂的对象系统调用Clone()是一个递归的过程,这次复制的对象和源对象是独立的。

浅度复制对引用类型只复制引用,及复制后源引用与目标引用的引用类型是指向都一个对象,操作其中一个,另外一个也会受影响;深度复制则源引用和目标引用对 象的是两个不同的对象,操作互不影响;浅度复制是直接完全把栈的内容拷贝一份而已,而深度复制把对应的堆内容也拷贝了一份;浅度复制可以直接调用 System.Object.MemberwiseClone()这个受保护的方法就可以了;而深度引用根据C#的规范实现ICloneable接口即可。

首先给出第一个使用System.Object.MemberwiseClone()实现浅度复制的实例,在Cloner中,我们只定义了值类型的成员变量Val:

public class Cloner {
    public int Val;
    public Cloner(int newVal) {
        Val = newVal;
    }

    public object GetCopy() {
        return MemberwiseClone();
    }
}

然后添加测试代码如下:

static void Main() {
    Cloner mySource = new Cloner(5);
    Cloner myTarget = (Cloner)mySource.GetCopy();
    Console.WriteLine("mySource.Val = {0}", mySource.Val);
    Console.WriteLine("myTarget.Val = {0}", myTarget.Val);
    mySource.Val = 2;
    Console.WriteLine("mySource.Val = {0}", mySource.Val);
    Console.WriteLine("myTarget.Val = {0}", myTarget.Val);
    Console.ReadKey();
}

程序的输出结果如下:

C#深度和浅度复制

我们发现,这种简单的值类型域的复制,即使改变源对象的值也不会影响复制对象的值,也就是说我们创建了一个新的对象实例,下面我们在来看一个包含引用类型的实例,实例代码和测试代码如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace Test5 {
    static class Program {
        public class Content {
            public int Val;
        }

        public class Cloner {
            public Content MyContent = new Content();
            public Cloner(int newVal) {
                MyContent.Val = newVal;
            }

            public object GetCopy() {
                return MemberwiseClone();
            }
        }
        /// <summary>
        /// 应用程序的主入口点。
        /// </summary>
        [STAThread]
        static void Main() {
            Cloner mySource = new Cloner(5);
            Cloner myTarget = (Cloner)mySource.GetCopy();
            Console.WriteLine("mySource.MyContent.Val = {0}", mySource.MyContent.Val);
            Console.WriteLine("myTarget.MyContent.Val = {0}", myTarget.MyContent.Val);
            mySource.MyContent.Val = 2;
            Console.WriteLine("mySource.MyContent.Val = {0}", mySource.MyContent.Val);
            Console.WriteLine("myTarget.MyContent.Val = {0}", myTarget.MyContent.Val);
            Console.ReadKey();
        }
    }
}

程序的执行结果如下:

C#深度和浅度复制

从结果中你可以发现,当改变源对象mySource.MyContent.Val的引用类型域的值后通过阴影复制得到的对象myTarget的引用类型域的值同时发生了 改变。这是因为使用MemberwiseClone()只能实现浅度复制,mySource.MyContent引用了与myTarget.MyContent相同的对象实例。为了实现彻底复制,即复制对象与源对象相互独立、互不影响,需要执行深度复制。修改上面的 GetCopy()方法也可以进行深度复制,但最好使用.NET Framework的标准方式。为此,实现ICloneable接口,该接口有一个方法Clone(),这个方法不带参数,返回一个对象类型,其签名和上 面使用的GetCopy()方法相同。下面给出实际的源代码和测试代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;

namespace Test5 {
    static class Program {
        public class Content {
            public int Val;
        }

        public class Cloner : ICloneable {
            public Content MyContent = new Content();
            public Cloner(int newVal) {
                MyContent.Val = newVal;
            }

            #region ICloneable 成员

            public object Clone() {
                Cloner clonedCloner = new Cloner(MyContent.Val);
                return clonedCloner;
            }

            #endregion
        }
        /// <summary>
        /// 应用程序的主入口点。
        /// </summary>
        [STAThread]
        static void Main() {
            Cloner mySource = new Cloner(5);
            Cloner myTarget = (Cloner)mySource.Clone();
            Console.WriteLine("mySource.MyContent.Val = {0}", mySource.MyContent.Val);
            Console.WriteLine("myTarget.MyContent.Val = {0}", myTarget.MyContent.Val);
            mySource.MyContent.Val = 2;
            Console.WriteLine("mySource.MyContent.Val = {0}", mySource.MyContent.Val);
            Console.WriteLine("myTarget.MyContent.Val = {0}", myTarget.MyContent.Val);
            Console.ReadKey();
        }
    }
}

程序的运行结果如下:

C#浅度和深度复制

从结果你可以看出,通过深度复制,两个对象已经没有任何关系了,对第一个对象的改变没有影响到第二个对象。这里使用的是包含在源Cloner对象(MyContent)中的Content对象的Val字段去创建一个新的Cloner对象。这个域是一个值类型所以不需要进行深度复制。通过ICloneable.Clone方法实现除MemberwiseClone所提供的克隆之外的克隆。

发表评论0条 】
网友评论(共?条评论)..
C#深度复制和浅度复制