被复制的对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都扔然指向原来的对象。
这里盗用刘老师的一张图
学习浅复制之前 我们要知道 string是一种拥有值类特点的特殊引用类型,MemberwiseClone()方法是这样的,如果字段是值类型的,则对该字段执行逐位复制,如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其复本引用同意对象。
先来说说MemberwiseClone()方法的使用方法吧
MemberwiseClone 方法通过创建一个新对象,然后将当前对象的非静态字段复制到新的对象来创建的浅表副本。如果字段为值类型,则执行该字段的位按位复制。如果字段为引用类型,则将引用复制但被引用的对象不 ;因此,原始对象和其克隆引用同一对象。
例如,考虑对象X引用对象 A 和 B , 对象 B,反过来,引用对象 c。X 的浅表副本创建新的对象 X 2 又引用对象 A 和 b。与此相反,X 的深层副本创建引用 A2 和 B2 是的一个副本的新对象的新对象 X 2 和 b。B2 反过来引用新对象 C2,这是 C 的副本。该示例阐释了浅层和深层复制操作之间的差异。
有多种方式来实现深层复制操作,如果浅表副本操作由执行 MemberwiseClone 方法不符合您的需求。这些要求包括:
调用的对象要复制可以使用来自第一个对象的属性值创建第二个对象的类构造函数。此操作假定对象的值完全由其类构造函数中定义。
调用 MemberwiseClone 方法以创建一个对象的浅表副本,然后将其值是与原始对象的任何属性或字段的值是引用类型相同的新对象。 DeepCopy 方法在示例中阐释了此方法。
序列化对象要深层复制,然后将序列化的数据还原到另一个对象变量。
具有递归使用反射来执行深层复制操作。
例子接着用上个博客的例子
这张图简历类和工作经历类是组合关系
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.age = age; this.sex = sex; } //设置工作经验 public void SetWorkWxperience(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(); } }
static void Main(string[] args) { //客户端代码调用 Resume a = new Resume("大鸟"); a.SetPersonalInfo("男", "29"); a.SetWorkWxperience("1998-2000", "XX公司"); Resume b = (Resume)a.Clone(); b.SetWorkWxperience("1998-2006", "YY公司"); //b和c都克隆于a,但当它们都设置了“工作经历”时,我们希望的结果是三个显示不一样 Resume c = (Resume)a.Clone(); c.SetPersonalInfo("男", "24"); c.SetWorkWxperience("1998-2003", "ZZ公司"); a.Display(); b.Display(); c.Display(); Console.Read(); }
由于是浅表复制,所以对于值类型,没什么问题,对引用类型,就只能复制和引用,对引用的对象还是指向了原来的对象,所以就会出现我给a、b、c三个引用设置‘工作经历’,但却同时看到三个引用都是最后的一次设置,因为三个引用指向了同一个对象。
开始的时候在这里非常不明白为什么显示的结果是最后一次设置的结果,而不是显示第一次设置的结果。所以就在网上各种搜啊,因为不能非常明确的表述出自己的意思,得到的答案,没有一个是自己想要的。后来问了一下网友,告诉我说是因为最后一个设置把a设置也覆盖了,同一段内存只能保存一个设置。
除了对象本身被复制外,对象所包含的所有成员变量也被复制。这里的深复制相对于浅复制来说的。
class WorkExperience : ICloneable //让“工作经历”实现IClone接口 { 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(); } private Resume(WorkExperience work) { this.work = (WorkExperience)work.Clone(); //提供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 = this.sex; obj.age = this.age; return obj; } }
static void Main(string[] args) { 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.SetWorkExperience("1998-2003", "ZZ企业"); a.Display(); b.Display(); c.Display(); Console.Read(); }