由浅拷贝讨论到深拷贝再讨论到接口(一):浅拷贝和深拷贝
接口ICloneable为我们实现了拷贝的梦想。
(一)如何实现浅拷贝?
新建学校对象(School),实现接口ICloneable,如果我们这样写,即完成了浅拷贝:return base.MemberwiseClone();
public class School : ICloneable
{
public object Clone()
{
return base.MemberwiseClone();
}
}
完整代码:
1 using System;
2
3 namespace NY
4 {
5 class Program
6 {
7 static void Main(string[] args)
8 {
9 School a = new School();
10 a.Id = 1;
11 a.Name = "A school";
12 a.Student.Name = "Li lei";
13
14 Console.WriteLine("-----------a对象原始值-----------");
15 Console.WriteLine("a.Id:" + a.Id);
16 Console.WriteLine("a.Name:" + a.Name);
17 Console.WriteLine("a.Student.Name:" + a.Student.Name);
18
19 School b = (School)a.Clone();
20
21 Console.WriteLine("-----------a对象当前值-----------");
22 Console.WriteLine("a.Id:" + a.Id);
23 Console.WriteLine("a.Name:" + a.Name);
24 Console.WriteLine("a.Student.Name:" + a.Student.Name);
25 Console.WriteLine("-----------b对象当前值-----------");
26 Console.WriteLine("b.Id:" + b.Id);
27 Console.WriteLine("b.Name:" + b.Name);
28 Console.WriteLine("b.Student.Name:" + b.Student.Name);
29 }
30 }
31
32 public class School : ICloneable
33 {
34 private int _id = 0;
35 private string _name = string.Empty;
36 private Student _student = new Student();
37
38 public int Id
39 {
40 get { return _id; }
41 set { _id = value; }
42 }
43 public string Name
44 {
45 get { return _name; }
46 set { _name = value; }
47 }
48 public Student Student
49 {
50 get { return _student; }
51 set { _student = value; }
52 }
53
54 public object Clone()
55 {
56 return base.MemberwiseClone();
57 }
58 }
59
60 public class Student : ICloneable
61 {
62 private string _name = string.Empty;
63
64 public string Name
65 {
66 get { return _name; }
67 set { _name = value; }
68 }
69
70 public object Clone()
71 {
72 return base.MemberwiseClone();
73 }
74 }
75 }
76
结果如下:
结论:
(1) 我们只用了一句话,即轻松地将a对象的值拷贝给了b对象:School b = (School)a.Clone();
(二)值类型--浅拷贝会创建副本
对于值类型int,我们把b对象Id的值修改为2(20行),看看会有什么结果
1 using System;
2
3 namespace NY
4 {
5 class Program
6 {
7 static void Main(string[] args)
8 {
9 School a = new School();
10 a.Id = 1;
11 a.Name = "A school";
12 a.Student.Name = "Li lei";
13
14 Console.WriteLine("-----------a对象原始值-----------");
15 Console.WriteLine("a.Id:" + a.Id);
16 Console.WriteLine("a.Name:" + a.Name);
17 Console.WriteLine("a.Student.Name:" + a.Student.Name);
18
19 School b = (School)a.Clone();
20 b.Id = 2;
21
22 Console.WriteLine("-----------a对象当前值-----------");
23 Console.WriteLine("a.Id:" + a.Id);
24 Console.WriteLine("a.Name:" + a.Name);
25 Console.WriteLine("a.Student.Name:" + a.Student.Name);
26 Console.WriteLine("-----------b对象当前值-----------");
27 Console.WriteLine("b.Id:" + b.Id);
28 Console.WriteLine("b.Name:" + b.Name);
29 Console.WriteLine("b.Student.Name:" + b.Student.Name);
30 }
31 }
32
33 public class School : ICloneable
34 {
35 private int _id = 0;
36 private string _name = string.Empty;
37 private Student _student = new Student();
38
39 public int Id
40 {
41 get { return _id; }
42 set { _id = value; }
43 }
44 public string Name
45 {
46 get { return _name; }
47 set { _name = value; }
48 }
49 public Student Student
50 {
51 get { return _student; }
52 set { _student = value; }
53 }
54
55 public object Clone()
56 {
57 return base.MemberwiseClone();
58 }
59 }
60
61 public class Student : ICloneable
62 {
63 private string _name = string.Empty;
64
65 public string Name
66 {
67 get { return _name; }
68 set { _name = value; }
69 }
70
71 public object Clone()
72 {
73 return base.MemberwiseClone();
74 }
75 }
76 }
77
结果如下:
结论:
(1) a对象Id的值仍然为1,b对象Id的值却为2,说明对于值类型来说,浅拷贝会创建副本。
(三)引用类型string--浅拷贝会创建副本
对于引用类型string,我们把b对象Name的值修改为b school(21行),看看会有什么结果
1 using System;
2
3 namespace NY
4 {
5 class Program
6 {
7 static void Main(string[] args)
8 {
9 School a = new School();
10 a.Id = 1;
11 a.Name = "A school";
12 a.Student.Name = "Li lei";
13
14 Console.WriteLine("-----------a对象原始值-----------");
15 Console.WriteLine("a.Id:" + a.Id);
16 Console.WriteLine("a.Name:" + a.Name);
17 Console.WriteLine("a.Student.Name:" + a.Student.Name);
18
19 School b = (School)a.Clone();
20 b.Id = 2;
21 b.Name = "B school";
22
23 Console.WriteLine("-----------a对象当前值-----------");
24 Console.WriteLine("a.Id:" + a.Id);
25 Console.WriteLine("a.Name:" + a.Name);
26 Console.WriteLine("a.Student.Name:" + a.Student.Name);
27 Console.WriteLine("-----------b对象当前值-----------");
28 Console.WriteLine("b.Id:" + b.Id);
29 Console.WriteLine("b.Name:" + b.Name);
30 Console.WriteLine("b.Student.Name:" + b.Student.Name);
31 }
32 }
33
34 public class School : ICloneable
35 {
36 private int _id = 0;
37 private string _name = string.Empty;
38 private Student _student = new Student();
39
40 public int Id
41 {
42 get { return _id; }
43 set { _id = value; }
44 }
45 public string Name
46 {
47 get { return _name; }
48 set { _name = value; }
49 }
50 public Student Student
51 {
52 get { return _student; }
53 set { _student = value; }
54 }
55
56 public object Clone()
57 {
58 return base.MemberwiseClone();
59 }
60 }
61
62 public class Student : ICloneable
63 {
64 private string _name = string.Empty;
65
66 public string Name
67 {
68 get { return _name; }
69 set { _name = value; }
70 }
71
72 public object Clone()
73 {
74 return base.MemberwiseClone();
75 }
76 }
77 }
78
结果如下:
结论:
(1) a对象Name的值仍然为a school,b对象Name的值却为b school,说明对于引用类型string来说,浅拷贝会创建副本。
(四)思考:如果把小写string改成大写String呢?
这里直接给出结论:
(1) 对于大写String来说,浅拷贝会创建副本。
(2) 由于在工作中常会用的是小写string,较少用大写String,所以,关于两者差异,请见笔者另一篇文章《浅析string与String》
(五)其他引用类型--浅拷贝不会创建副本
对于其他new出来的引用类型,我们把b对象的Student的Name的值修改为Han meimei(22行),看看会有什么结果
1 using System;
2
3 namespace NY
4 {
5 class Program
6 {
7 static void Main(string[] args)
8 {
9 School a = new School();
10 a.Id = 1;
11 a.Name = "A school";
12 a.Student.Name = "Li lei";
13
14 Console.WriteLine("-----------a对象原始值-----------");
15 Console.WriteLine("a.Id:" + a.Id);
16 Console.WriteLine("a.Name:" + a.Name);
17 Console.WriteLine("a.Student.Name:" + a.Student.Name);
18
19 School b = (School)a.Clone();
20 b.Id = 2;
21 b.Name = "B school";
22 b.Student.Name = "Han meimei";
23
24 Console.WriteLine("-----------a对象当前值-----------");
25 Console.WriteLine("a.Id:" + a.Id);
26 Console.WriteLine("a.Name:" + a.Name);
27 Console.WriteLine("a.Student.Name:" + a.Student.Name);
28 Console.WriteLine("-----------b对象当前值-----------");
29 Console.WriteLine("b.Id:" + b.Id);
30 Console.WriteLine("b.Name:" + b.Name);
31 Console.WriteLine("b.Student.Name:" + b.Student.Name);
32 }
33 }
34
35 public class School : ICloneable
36 {
37 private int _id = 0;
38 private string _name = string.Empty;
39 private Student _student = new Student();
40
41 public int Id
42 {
43 get { return _id; }
44 set { _id = value; }
45 }
46 public string Name
47 {
48 get { return _name; }
49 set { _name = value; }
50 }
51 public Student Student
52 {
53 get { return _student; }
54 set { _student = value; }
55 }
56
57 public object Clone()
58 {
59 return base.MemberwiseClone();
60 }
61 }
62
63 public class Student : ICloneable
64 {
65 private string _name = string.Empty;
66
67 public string Name
68 {
69 get { return _name; }
70 set { _name = value; }
71 }
72
73 public object Clone()
74 {
75 return base.MemberwiseClone();
76 }
77 }
78 }
79
结果如下:
结论:
(1) 大家注意看,a.Studnet.Name也被改成Han meimei了,说明,对于引用类型,浅拷贝不会创建副本。
(六)思考:我们修改b对象的时候,不希望把a对象的值也给改了
上例的结论,也许并不是我们期望的,我们期望当b.Student.Name改成Han meimei的时候,不要改变a.Student.Name的值,因此,我们引入深拷贝的概念。
(七)如何实现深拷贝
既然ICloneable接口已经提供了Clone方法,那么,我们只需要重写这个方法,即可实现深拷贝。
public class School : ICloneable
{
public object Clone()
{
School cloned = new School();
cloned.Id = this._id;
cloned.Name = this._name;
cloned.Student = (Student)this._student.Clone();
return cloned;
}
}
完整代码:
1 using System;
2
3 namespace NY
4 {
5 class Program
6 {
7 static void Main(string[] args)
8 {
9 School a = new School();
10 a.Id = 1;
11 a.Name = "A school";
12 a.Student.Name = "Li lei";
13
14 Console.WriteLine("-----------a对象原始值-----------");
15 Console.WriteLine("a.Id:" + a.Id);
16 Console.WriteLine("a.Name:" + a.Name);
17 Console.WriteLine("a.Student.Name:" + a.Student.Name);
18
19 School b = (School)a.Clone();
20 b.Id = 2;
21 b.Name = "B school";
22 b.Student.Name = "Han meimei";
23
24 Console.WriteLine("-----------a对象当前值-----------");
25 Console.WriteLine("a.Id:" + a.Id);
26 Console.WriteLine("a.Name:" + a.Name);
27 Console.WriteLine("a.Student.Name:" + a.Student.Name);
28 Console.WriteLine("-----------b对象当前值-----------");
29 Console.WriteLine("b.Id:" + b.Id);
30 Console.WriteLine("b.Name:" + b.Name);
31 Console.WriteLine("b.Student.Name:" + b.Student.Name);
32 }
33 }
34
35 public class School : ICloneable
36 {
37 private int _id = 0;
38 private string _name = string.Empty;
39 private Student _student = new Student();
40
41 public int Id
42 {
43 get { return _id; }
44 set { _id = value; }
45 }
46 public string Name
47 {
48 get { return _name; }
49 set { _name = value; }
50 }
51 public Student Student
52 {
53 get { return _student; }
54 set { _student = value; }
55 }
56
57 public object Clone()
58 {
59 School cloned = new School();
60 cloned.Id = this._id;
61 cloned.Name = this._name;
62 cloned.Student = (Student)this._student.Clone();
63 return cloned;
64 }
65 }
66
67 public class Student : ICloneable
68 {
69 private string _name = string.Empty;
70
71 public string Name
72 {
73 get { return _name; }
74 set { _name = value; }
75 }
76
77 public object Clone()
78 {
79 Student cloned = new Student();
80 cloned.Name = this._name;
81 cloned.Age = this._age;
82 return cloned;
83 }
84 }
85 }
86
结果如下:
结论:
(1) 深拷贝为我们实现了对象的完整复制,彻底解决了浅拷贝对于引用类型只拷贝地址带来的问题。
(八)或许有人就要问了
常常听博客园高手说针对接口编程,但,到我自己使用的时候,想不到要用接口。比如这个例子,不设计接口,我也能实现深拷贝,比如在Framework层新写个方法MyClone(),然后这样调用School b = (School)MyClone(a);。那为什么还要去设计接口呢?给谁用呢?怎么用呢?为什么比不设计接口要好呢?
下篇博文,会有精彩解答,敬请期待!