浅拷贝
浅拷贝拷贝的是对象的引用,实际上还是指向同一个对象,改变其中任意对象的值,另一个对象的值也会跟着改变。
public class Animal
{
public string Name { get; set; }
}
Animal a1 = new Animal() { Name = "小狗" };
Animal a2 = a1; // 这里就是浅拷贝
a2.Name = "小狗钱钱"; // 改变 a2.Name 的值
Console.WriteLine(a1.Name); // 小狗钱钱,a1 跟着改变了,因为它们都指向同一个对象
Console.WriteLine(a2.Name); // 小狗钱钱
深拷贝
简单的深拷贝
深拷贝就是连被引用的对象也拷贝一份出来,改变新拷贝对象的值不会影响原来对象的值。.Net中所有类型的根类定义了一个 protected 方法MemberwiseClone()
注 ,使用这个方法可以实现简单的深拷贝:
public class Employee
{
public string Name { get; set; }
public Employee Copy() => this.MemberwiseClone() as Employee;
}
Employee e1 = new Employee() { Name = "张三" };
Employee e2 = e1.Copy();
e2.Name = "李四";
Console.WriteLine(e1.Name); // 张三
Console.WriteLine(e2.Name); // 李四
Console.WriteLine(ReferenceEquals(e1, e2)); // False
由上面可以看出,改变拷贝对象(e2所指向的对象)的值不会影响 e1 所指向对象的值,再通过ReferenceEquals
方法比较两个对象的引用,同样不相同,所以确实实现了“深拷贝”。
完全深拷贝
上面的深拷贝对象只有简单数据类型,如果引用了复杂的对象,那么MemberwiseClone
方法还能完成“深拷贝”吗?
public class Employee
{
public string Name { get; set; }
public Dog Dog { get; set; } // 新增复杂对象
public Employee Copy() => this.MemberwiseClone() as Employee;
}
public class Dog
{
public string Name { get; set; }
}
Dog dog = new Dog() { Name = "小狗钱钱" };
Employee e1 = new Employee() { Name = "张三", Dog = dog };
Employee e2 = e1.Copy();
e2.Name = "李四";
Console.WriteLine(e1.Name); // 张三
Console.WriteLine(e2.Name); // 李四
Console.WriteLine(ReferenceEquals(e1, e2)); // False
// 新增比较
e1.Dog.Name = "小狗";
Console.WriteLine(e1.Dog.Name); // 小狗
Console.WriteLine(e2.Dog.Name); // 小狗
Console.WriteLine(ReferenceEquals(e1.Dog, e2.Dog)); // True
由上面新增比较可以看出,引用了复杂对象类型的属性,使用MemberwiseClone
方法进行拷贝时,并不能实现“完全的深拷贝”。实现“完全深拷贝”可以通过二进制格式序列化和反序列化的方式:
public static object Clone(object obj)
{
using (MemoryStream ms = new MemoryStream()) // 创建内存流
{
BinaryFormatter bf = new BinaryFormatter(); // 以二进制格式进行序列化
bf.Serialize(ms, obj);
ms.Seek(0, 0);
object res = bf.Deserialize(ms); // 反序列化当前实例到一个object
return res;
}
}
Employee e3 = Clone(e1) as Employee; // 把 e1 拷贝到 e3
e1.Dog.Name = "小狗2";
Console.WriteLine(e1.Dog.Name); // 小狗2
Console.WriteLine(e3.Dog.Name); // 小狗1
Console.WriteLine(ReferenceEquals(e1, e3)); // False
Console.WriteLine(ReferenceEquals(e1.Dog, e3.Dog)); // False
好了,到这里可以看到,我们已经实现了“完全深拷贝”。
值类型的复制都可以看做是深拷贝。
References
MemberwiseClone
创建一个浅表副本。过程是创建一个新对象,然后将当前对象的非静态字段复制到该新对象。如果字段是值类型,则对该字段执行逐位复制;如果字段是引用类型,则复制引用但不复制引用对象。