1、浅拷贝与深拷贝的定义
什么是拷贝?拷贝即为常说的复制或者克隆一个对象,并且通过拷贝这些源对象创建新的对象。其中拷贝分为浅拷贝和深拷贝。对于拷贝出来的对象,在使用上有很大的差异,特别是在引用类型上。
浅拷贝:将对象中的所有字段复制到新的对象中。其中,值类型字段被复制到新对象中后,在新对象中的修改不会影响到原先对象的值。而新对象的引用类型则是原先对象引用类型的引用,不是引用自己对象本身。注:在新对象中修改引用类型的值会影响到原先对象;理论上String也是引用类型,但是由于由于该类型比较特殊,Object.MemberwiseClone()方法依旧为其新对象开辟了新的内存空间存储String的值,在浅拷贝中把String类型当作'值类型'即可。
深拷贝:同样也是拷贝,但是与浅拷贝不同的是,深拷贝会对引用类型重新在创新一次(包括值类型),在新对象做的任何修改都不会影响到源对象本身。
2、实现浅拷贝与深拷贝
在 .Net平台开发中,要实现拷贝,微软官方建议继承ICloneable接口,该接口位于System命名空间下,该接口只实现一个Clone方法,我们可以根据具体项目需求在该方法内实现浅拷贝或者深拷贝。先实现一个浅拷贝,具体代码如下:
//Equal探索 static void Main() { //创建源对象 Teacher Source = new Teacher("Fode",18,DateTime.Now,22); Source.Print("源对象"); //浅拷贝对象 Teacher Target = Source.Clone() as Teacher; /* 理论上String也是引用类型,但是由于由于该类型比较特殊, Object.MemberwiseClone()方法依旧为其新对象开辟了新的内存空间存储String的值, 在浅拷贝中把String类型当作'值类型'即可 */ Target.Name = "JJ"; Target.Student.Count = 11; Console.WriteLine("新对象的引用类型的值发生变化"); Target.Print("新对象"); Source.Print("源对象"); Console.ReadKey(); } class Teacher : ICloneable { public Teacher(String name, Int32 age, DateTime birthday, Int32 count) { this._name = name; this._age = age; this._birthday = birthday; this.Student = new Student() { Count = count }; } private Int32 _age; public Int32 Age { get { return _age; } set { _age = value; } } private String _name; public String Name { get { return _name; } set { _name = value; } } private DateTime _birthday; public DateTime Birthday { get { return _birthday; } set { _birthday = value; } } public Student Student { get; set; } public void Print(String title) { Console.WriteLine(title); Console.WriteLine($"基本信息:姓名:{this.Name},年龄:{this.Age},生日:{this.Birthday.ToString("D")}"); Console.WriteLine($"引用类型的值{Student.ToString()}"); Console.WriteLine(); } //实现浅拷贝 public Object Clone() { return this.MemberwiseClone(); } } class Student { public Int32 Count { get; set; } public override string ToString() { return Count.ToString(); } }
其运行结果如下:
可以发现当新对象的引用类型发生改变后,其源对象的引用类型也发生改变(String类型除外),他们共同引用的是Student这个引用类型对象(即使发生变化的是其里面的值类型),而新对象的值类型改变并不会到源类型的值类型。
而对于要实现深拷贝则有很多中方法了,比如在拷贝方法里面 直接一个个属性字段赋值,但是一旦为源对象新增属性或者字段的时候,容易忘了修改拷贝方法中的值,最好使用序列化的方法进行深拷贝。深拷贝简单代码如下,与浅拷贝同样的案例,只是重写了Clone()方法,并在类加了[Serializable]序列化特性标签:
[Serializable] class Teacher : ICloneable { public Teacher(String name, Int32 age, DateTime birthday, Int32 count) { this._name = name; this._age = age; this._birthday = birthday; this.Student = new Student() { Count = count }; } private Int32 _age; public Int32 Age { get { return _age; } set { _age = value; } } private String _name; public String Name { get { return _name; } set { _name = value; } } private DateTime _birthday; public DateTime Birthday { get { return _birthday; } set { _birthday = value; } } public Student Student { get; set; } public void Print(String title) { Console.WriteLine(title); Console.WriteLine($"基本信息:姓名:{this.Name},年龄:{this.Age},生日:{this.Birthday.ToString("D")}"); Console.WriteLine($"引用类型的值{Student.ToString()}"); Console.WriteLine(); } //实现深拷贝拷贝 public Object Clone() { System.IO.Stream stream = new MemoryStream(); try { System.Runtime.Serialization.IFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); formatter.Serialize(stream, this); stream.Seek(0, SeekOrigin.Begin); return formatter.Deserialize(stream); } finally { stream.Close(); stream.Dispose(); } } } [Serializable] class Student { public Int32 Count { get; set; } public override string ToString() { return Count.ToString(); } } static void Main() { //创建源对象 Teacher Source = new Teacher("Fode", 18, DateTime.Now, 22); Source.Print("源对象"); //深拷贝对象 Teacher Target = Source.Clone() as Teacher; Target.Name = "JJ"; Target.Student.Count = 11; Console.WriteLine("新对象的引用类型的值发生变化"); Target.Print("新对象"); Source.Print("源对象"); Console.ReadKey(); }
其结果如下:
可以发现,此时拷贝后的Target对象与源对象没有任何关系。修改源对象的引用类型并不会影响对应新对象的值。最后在把代码优化一下,在一个类中同时实现深拷贝和浅拷贝:
//实现浅拷贝 public Object Clone() { return this.MemberwiseClone(); } /// <summary> /// 获得浅拷贝对象 /// </summary> /// <returns></returns> public Teacher ShallowClone() { return this.Clone() as Teacher; } /// <summary> /// 获得深拷贝对象 /// </summary> /// <returns></returns> public Teacher DeepClone() { System.IO.Stream stream = new MemoryStream(); try { System.Runtime.Serialization.IFormatter formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); formatter.Serialize(stream, this); stream.Seek(0, SeekOrigin.Begin); return formatter.Deserialize(stream) as Teacher; } finally { stream.Close(); stream.Dispose(); } }
注:ICloneable接口适用于
.NET Core
.NET Framework
.NET Standard
Xamarin.Android
Xamarin.iOS
Xamarin.Mac
Teacher Source = new Teacher("Fode", 18, DateTime.Now, 22); Teacher Target = Source; Source.Print("源对象"); Console.WriteLine("源对象的值类型发生改变"); Source.Name = "JJ"; Source.Age = 22; Source.Print("源对象"); Target.Print("新对象"); Console.WriteLine("新对象的值类型发生改变"); Target.Name = "范冰冰"; Target.Age = 18; Source.Print("源对象"); Target.Print("新对象"); Console.ReadKey();
输出结果如下:
来源:https://www.cnblogs.com/fode/p/10073675.html