浅拷贝与深拷贝
前言
本文以C#为例,说明什么是浅拷贝,什么是深拷贝,以及如何应用序列化快速深拷贝。
目录
- 浅拷贝与深拷贝简单说明
- 浅拷贝
- 复杂对象的浅拷贝
- 深拷贝
- 通过序列化深拷贝
- 参考
浅拷贝与深拷贝简单说明
无论是浅拷贝与深拷贝,C#都将源对象中的所有字段复制到新的对象中。不过,对于值类型字段,引用类型字段以及字符串类型字段的处理,两种拷贝方式存在一定的区别。
拷贝类型 | 字段 | 拷贝操作详情 | 副本或源对象中修改是否相互影响 |
---|---|---|---|
浅拷贝 | 值类型 | 字段被拷贝到副本中 | 不会 |
浅拷贝 | 引用类型 | 字段被拷贝到副本中 | 会 |
深拷贝 | 值类型 | 字段被重新创建并赋值 | 不会 |
深拷贝 | 引用类型 | 字段被重新创建并赋值 | 不会 |
浅拷贝
浅拷贝可以利用MemberwiseClone方法实现,具体代码如下:
using System;
namespace CopyTest
{
public class Person : ICloneable
{
public string Name { get; set; }
public int Age { get; set; }
public Object Clone()
{
return this.MemberwiseClone() ;
}
}
}
using System;
namespace CopyTest
{
public class PersonTest
{
public static void Test()
{
Test1();
Test2();
}
public static void Test1()
{
Person p1 = new Person();
p1.Name = "zhao";
p1.Age = 20;
Person p2 = p1;
p2.Name = "li";
Console.WriteLine(p1.Name);
}
public static void Test2()
{
Person p1 = new Person();
p1.Name = "zhao";
p1.Age = 20;
Person p2 = p1.Clone() as Person;
p2.Name = "li";
Console.WriteLine(p1.Name);
}
}
}
输出:
li
zhao
可见,通过浅拷贝,修改对象的值类型属性或字符串不影响拷贝对象;不浅拷贝只=是不行的。
复杂对象的浅拷贝
所谓复杂对象,指的是对象里面包含了对象(引用类型),而不是简单的值类型和字符串。比如:
using System;
namespace CopyTest
{
public class Car:ICloneable
{
public string Name { get; set; }
public Person Driver { get; set; }
public object Clone()
{
return this.MemberwiseClone();
}
}
}
这种情况下的浅拷贝是不会拷贝引用类型的。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CopyTest
{
class CarTest
{
public static void Test()
{
Car c1 = new Car();
c1.Name = "audi";
Person p1 = new Person();
p1.Name = "wang";
p1.Age = 20;
c1.Driver = p1;
Car c2 = c1.Clone() as Car;
c2.Name = "bmw";
c2.Driver.Name = "li";
Console.WriteLine(c1.Driver.Name);
}
}
}
输出
audi li
修改driver对c1有影响。解决这种问题,需要用深拷贝。
深拷贝
深拷贝需要单独写方法,代码如下:
using System;
namespace CopyTest
{
public class NewCar : ICloneable
{
public string Name { get; set; }
public Person Driver { get; set; }
public object Clone()
{
NewCar ret = new NewCar();
ret.Name = this.Name;
ret.Driver = new Person();
ret.Driver.Name = this.Driver.Name;
ret.Driver.Age = this.Driver.Age;
return ret;
}
}
}
测试代码
using System;
namespace CopyTest
{
public class NewCarTest
{
public static void Test()
{
NewCar c1 = new NewCar();
c1.Name = "audi";
Person p1 = new Person();
p1.Name = "wang";
p1.Age = 20;
c1.Driver = p1;
NewCar c2 = c1.Clone() as NewCar;
c2.Name = "bmw";
c2.Driver.Name = "li";
Console.WriteLine(c1.Driver.Name);
}
}
}
输出:
audi wang
通过序列化深拷贝
序列号的基本原理在这里不介绍了,我们可以应用序列号快速进行深拷贝,修改后的代码如下:
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
namespace CopyTest
{
[Serializable]
public class NewCar : ICloneable
{
public string Name { get; set; }
public Person Driver { get; set; }
public object Clone()
{
using (Stream objectStream = new MemoryStream())
{
IFormatter formatter = new BinaryFormatter();
formatter.Serialize(objectStream, this);
objectStream.Seek(0, SeekOrigin.Begin);
return formatter.Deserialize(objectStream);
}
}
}
}