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