一、前言
在C#中呢我们经常会看到或者听到这样的专业术语——深复制和浅复制,当然我也是在学习的过程中遇到了这两个概念,我就在这浅显的解析一下:
很多人对于这两个概念有这样不完全的理解:浅复制对于引用类型拷贝地址,对于值类型直接拷贝
二、概念对比
对象的拷贝也就是从现有对象复制一个“一模一样”的新对象出来。虽然都是复制对象,但是不同的复制方法,复制出来的新对象却并非完全一模一样,对象内部存在着一些差异。通常的复制方法有两种,即深拷贝和浅拷贝,那二者之间有何区别呢?MSDN里对 IClone接口的Clone方法有这样的说明:在深层副本中,所有的对象都是重复的;而在浅表副本中,只有顶级对象是重复的,并且顶级以下的对象包含引用。可以看出,深复制和浅复制之间的区别在于是否复制了子对象。这如何理解呢?下面我通过带有子对象的代码来验证二者的区别。
提示:在下面的代码中ICloneable 接口是在System命名空间中提供的,其中已只有一个唯一的一个方法 Clone();
三、实例
浅复制部分
<span style="font-size:24px;"> class WorkExperience { private string workDate; public string WorkDate //定义一个属性 { get { return workDate; } set { workDate = value; } } private string company; public string Company { get { return company; } set { company = value; } } } //简历类 class Resume : ICloneable { private string name; private string sex; private string age; private WorkExperience work; //引用工作经历对象 public Resume(string name) //构造函数 并且在类实例化是同时实例化工作经历 { this.name = name; work = new WorkExperience(); } //设置个人信息 public void SetPersonalInfo(string sex, string age) { this.sex = sex; this.age = age; } //设置工作经历 public void SetWorkExperience(string workDate, string company) { work.WorkDate = workDate; work.Company = company; } //显示 public void Display() { Console.WriteLine("{0} {1} {2}", name, sex, age); Console.WriteLine("{0} {1}", work.WorkDate, work.Company); } public object Clone() { return (object)this.MemberwiseClone(); } }</span>
<span style="font-size:24px;"> Resume a = new Resume("大鸟"); a.SetPersonalInfo("男", "29"); a.SetWorkExperience("1998-2000", "xx公司"); Resume b = (Resume)a.Clone(); b.SetWorkExperience("1998-2006", "YY公司"); Resume c = (Resume)a.Clone(); c.SetPersonalInfo("男", "24"); c.SetWorkExperience("1998-2003", "zz企业"); a.Display(); b.Display(); c.Display(); Console.Read();</span
运行结果是
而我们希望达到的效果是这样的:
这种差异就是我们在写代码的过程中没有注意到深复制和浅复制的区别,对于引用类型,就是复制了引用,对引用对象还是指向了原来的对象!
深复制部分
class WorkExperience : ICloneable //让工作经历实现ICloneable接口 { private string workDate; public string WorkDate //定义一个属性 { get { return workDate; } set { workDate = value; } } private string company; public string Company { get { return company; } set { company = value; } } public object Clone() //工作经历类,实现克隆方法 { return (object)this.MemberwiseClone(); } } //简历类 class Resume : ICloneable { private string name; private string sex; private string age; private WorkExperience work; //引用工作经历对象 public Resume(string name) //构造函数 并且在类实例化是同时实例化工作经历 { this.name = name; work = new WorkExperience(); } //提供Clone 方法调用的私有构造函数,以便克隆工作经历 的数据 private Resume(WorkExperience work) { this.work = (WorkExperience)work.Clone(); } //设置个人信息 public void SetPersonalInfo(string sex, string age) { this.sex = sex; this.age = age; } //设置工作经历 public void SetWorkExperience(string workDate, string company) { work.WorkDate = workDate; work.Company = company; } //显示 public void Display() { Console.WriteLine("{0} {1} {2}", name, sex, age); Console.WriteLine("{0} {1}", work.WorkDate, work.Company); } //调用私有的构造方法,让 工作经历 克隆完成,然后在给这个简历 对象的相关字段赋值 //最终返回一个深赋值的简历对象 public object Clone() { Resume obj = new Resume(this.work); obj.name = this.name; obj.sex = sex; obj.age = age; return obj; } }
//深复制和浅复制的客户端代码相同 Resume a = new Resume("大鸟"); a.SetPersonalInfo("男", "29"); a.SetWorkExperience("1998-2000", "xx公司"); Resume b = (Resume)a.Clone(); b.SetWorkExperience("1998-2006", "YY公司"); Resume c = (Resume)a.Clone(); c.SetPersonalInfo("男", "24"); c.SetWorkExperience("1998-2003", "zz企业"); a.Display(); b.Display(); c.Display();
在这段代码中我们实现了预期的效果!
我们在从两个比较简单的图来认识一下:
四、总结:
从以上结果可以看出,深拷贝时两个对象是完全“分离”的,改变其中一个,不会影响到另一个对象;浅拷贝时两个对象并未完全“分离”,改变顶级对象的内容,不会对另一个对象产生影响,但改变子对象的内容,则两个对象同时被改变。这种差异的产生,即是取决于拷贝子对象时复制内存还是复制指针。深拷贝为子对象重新分配了一段内存空间,并复制其中的内容;浅拷贝仅仅将指针指向原来的子对象。