C#中关于深拷贝及浅拷贝的总结
一:首先定义要处理的类:
View Code
1 [Serializable]
2 public class Car
3 {
4 public string name = "QQ";
5 }
6
7 [Serializable]
8 public class Person
9 {
10 public Car myCar;
11
12 string name;
13
14 public string Name
15 {
16 get { return name; }
17 set { name = value; }
18 }
19 int age;
20
21 public int Age
22 {
23 get { return age; }
24 set { age = value; }
25 }
26 char gender;
27
28 public char Gender
29 {
30 get { return gender; }
31 set { gender = value; }
32 }
33
34 public Person() { }
35
36 public Person(string name, int age, char gender)
37 {
38 this.name = name;
39 this.age = age;
40 this.gender = gender;
41 //调用Person有三个参数的构造方法后,将Car在这里初始化,就使得myCar.name="QQ"了,即p1调用三个参数的构造方法后,可完全初始化
42 myCar = new Car();
43 }
44 }
二:示例处理代码:
View Code
1 #region Situation One(两个Person变量指向同一个Person对象,不存在深考及浅考)
2 Person p1 = new Person("Jason", 21, 'M');
3 Person p2 = p1;
4 #endregion
5
6
7 #region Situation Two(浅考就是不考虑内部的引用类型的拷贝,值类型的值拷贝,引用类型的值不拷贝)
8 Person p1 = new Person("Jason", 21, 'M');
9 //创建一个新的对象,将p1的值复制过来
10 Person p3 = new Person();//又创建了一个实例对象!
11 //值类型全部拷贝过来
12 p3.Name = p1.Name;
13 p3.Age = p1.Age;
14 p3.Gender = p1.Gender;
15 //使p3.myCar的地址指向p1.myCar的地址。
16 p3.myCar = p1.myCar;
17 //改变p1的值类型,p3不受影响
18 p1.Name = "Bill";
19 //p1及p3公共指向的内容,所以改变后,两者myCar.name结果一样
20 p1.myCar.name = "兰博基尼";
21 #endregion
22
23
24 #region Situation Three(深考就是将值类型及引用类型的值全部拷贝,注意:两Person变量不是指向同一个实例:即不是Situation One),下面实现 的缺点:即Person类中只有一个引用类型,如果引用类型中再嵌套引用类型就麻烦了,所以用Situation Four
25 Person p1 = new Person("Jason", 21, 'M');
26 Person p4 = new Person();
27 p4.Name = p1.Name;
28 p4.Age = p1.Age;
29 p4.Gender = p1.Gender;
30 //又new了一个Car对象
31 p4.myCar = new Car();
32 //注意:这里是将p1.myCar.name的值直接复制一份给p4.myCar.name,而不是将p1.myCar的地址复制一份给p4.myCar,所以当p1.myCar.name赋值为"凯迪拉克"后,p4.myCar.name它的值依旧为QQ
33 p4.myCar.name = p1.myCar.name;
34
35 p1.Name = "Nina";
36 p1.myCar.name = "凯迪拉克";
37 #endregion
38
39 #region 使用序列化,将对象保存到内存中,再在那块内存中进行反序列化,得到一个新的对象,与以前p1的那个对象一点关系也没有了
40 Person p1 = new Person("Jason", 21, 'M');
41 Person p5;
42 using (MemoryStream ms = new MemoryStream())
43 {
44 BinaryFormatter bf = new BinaryFormatter();
45 bf.Serialize(ms, p1);
46 //序列化结束后,将指针归零,方便接下来的从首位置的反序列化
47 ms.Position = 0;
48 p5 = (Person)bf.Deserialize(ms);
49 }
50
51 p1.Name = "Tony";
52 p1.myCar.name = "奥迪A6";
53 #endregion
三:问题的分析:
One: 将p1直接赋值给p2,既不是浅拷贝,也不是深拷贝,因为他们指向同一个对象。
Two:浅拷贝[只拷贝值类型数据,对象里面的引用类型还是指向同一个对象]
注:在分析“装箱与拆箱”、“深拷贝与浅拷贝”问题时,将string类型数据当做值类型处理即可!
单独为p3分配了内存空间,此时将p1的属性值全都拷贝了过来,也将引用类型变量进行了拷贝。p3.myCar=p1.myCar;因此p1与p3的myCar变量是指向的同一个Car对象。当p1.Name="Bill",p1.myCar.name="兰博基尼"后,p3中的name依旧是Jason,而p1.myCar.name得值也会跟着改变,不是QQ,而是兰博基尼。
大致关系图如下:
Three:深拷贝[不但拷贝值类型,对象里面的引用也进行了拷贝,产生一个新对象]
首先为p4分配了内存空间,先将p1的属性值全都拷贝过来,然而此时不再是p3.myCar=p1.mycar;而是p4中的myCar对象单独创建了对象(p4.myCar=new car();)再将p1中myCar的name赋值过来,此时p4与p1没有一点关系了,改变各自内部成员的值都不会影响对象的值。
Four:深拷贝实现的高级做法
当在p1中存在多个引用类型变量的时候,普通深拷贝很难应付了。此时,采用内存流的手段,将p1这个对象进行序列化,然后反序列化将产生一个新的对象,用p5接收,此时p5与p1一点关系也没有了,只是拷贝了值而已!