.NET基础知识之八——深拷贝,浅拷贝
目录
1、概念
2、使用赋值符号“=”
3、浅复制
4、深复制
5、问题一:如果类里面嵌套有多个类,然后嵌套类里面又嵌套类,那么像上面实现深拷贝的方法还能用吗?
6、问题二:实现深拷贝时,一定要实现ICloneable接口吗?
1、概念:
浅拷贝是将一个对象里面的所有字段重新拷贝一个到另外一个对象中去,如果字段是值类型,那么它拷贝的就是值,如果字段类型是引用类型,那么它拷贝的就是地址;
深拷贝是将一个对象里面的所有字段重新拷贝到另外一个对象中去,它与浅拷贝的区别是,如果字段是引用类型,它拷贝的不是地址,它会把引用类型里面每一个值重新拷贝一份,那就是说完全重新拷贝一个,新对象中的值不管怎么改变都不会影响以前的对象值。
我们平时所知道的赋值符号“=”,如果是值类型,它就是拷贝值类型,如果是引用类型,它就是赋予的地址。
由上面可以看出,“=”,浅拷贝,深拷贝对于值类型来说是没有区别的。针对引用类型来说是有区别的,看下面的例子。
先定义一个People类,在People类里面有一个Address类,先看Address类:
1: [Serializable]
2: public class Address
3: {
4: private string addressStr;
5:
6: public string AddressStr
7: {
8: get { return addressStr; }
9: set { addressStr = value; }
10: }
11:
12: private string addressNo;
13:
14: public string AddressNo
15: {
16: get { return addressNo; }
17: set { addressNo = value; }
18: }
19:
20: public override string ToString()
21: {
22: return AddressStr + ";" + AddressNo;
23: }
24: }
再来看People类:
1: [Serializable]
2: public class People:ICloneable
3: {
4: private string strName;
5:
6: public string StrName
7: {
8: get { return strName; }
9: set { strName = value; }
10: }
11:
12: private int strNo;
13:
14: public int StrNo
15: {
16: get { return strNo; }
17: set { strNo = value; }
18: }
19:
20: private Address addStr;
21:
22: public Address AddStr
23: {
24: get
25: {
26: if (addStr == null)
27: {
28: addStr = new Address();
29: }
30: return addStr;
31: }
32: set { addStr = value; }
33: }
34:
35: public override string ToString()
36: {
37: return StrName + ";" + strNo + addStr.ToString();
38: }
39: }
下面分三种情况来分析:
2、使用赋值符号“=”
赋值符号,只是把引用类型的地址赋给了另外一个对象,对这个对象里面的任何修改都会对原对象产生影响。
1: static void Main(string[] args)
2: {
3: People people = new People()
4: {
5: StrName = "张三",
6: StrNo = 1,
7: AddStr = new Address { AddressNo = "10010", AddressStr = "北京市海淀区" }
8: };
9:
10: //使用等于号"="赋值的情况
11: People peopleZhang = new People();
12: peopleZhang = people;
13: Console.WriteLine("情况1(使用等于号' = '赋值的情况)原始值是:" + peopleZhang.ToString());
14: peopleZhang.StrNo = 2;
15: peopleZhang.AddStr.AddressNo = "432000";
16: Console.WriteLine("情况1(使用等于号' = '赋值的情况)修改后peopleZhang的值是:" + people.ToString());
17: Console.WriteLine("情况1(使用等于号' = '赋值的情况)修改后people的值是:" + people.ToString());
18: Console.ReadLine();
输出结果如下:

3、浅复制
浅复制是把对象的每个字段拷贝了一份给另外一个对象,如果这个引用对象里面有值类型,那么拷贝的是值类型的副本,如果是引用类型那么拷贝的是对象的地址。也就是说拷贝后的对象,如果修改对象里面的值类型不会对原对象的值类型的值有影响,但是如果修改对象里面引用类型的值,那么原有对象引用类型的值也会跟着改变。
在People里面实现浅拷贝,可以自己实现,.NET里面也提供了一个方法MemberwiseClone,这个方法默认就是返回当前对象的副本,所以浅拷贝可以这样实现:
1: public People ShallowCopy()
2: {
3: return (People)this.MemberwiseClone();
4: }
前台调用如下:
1: //使用浅克隆的情况
2: People peopleWang = new People();
3: peopleWang = people.ShallowCopy();
4: Console.WriteLine("情况2(使用浅克隆的情况)原始值是:" + peopleWang.ToString());
5: peopleWang.StrNo = 3;
6: peopleWang.AddStr.AddressNo = "1234567";
7: Console.WriteLine("情况2(使用浅克隆的情况)修改后peopleWang的值是:" + peopleWang.ToString());
8: Console.WriteLine("情况2(使用浅克隆的情况)修改后people的值是:" + people.ToString());
9: Console.ReadLine();
输出结果如下:

4、深复制
深复制是把对象的每个字段拷贝了一份给另外一个对象,如果这个引用对象里面有值类型,那么拷贝的是值类型的副本,如果是引用类型那么拷贝的不是对象的地址,而是把具体的值也拷贝一遍。也就是说拷贝后的对象,如果修改对象里面的值类型不会对原对象的值类型的值有影响,修改对象里面引用类型的值,也不会对原有对象引用类型的值有影响。
实现深拷贝一般是实现ICloneable接口,具体实现方法如下:
1: public Object Clone()
2: {
3: People p = new People();
4: p.StrName = this.strName;
5: p.StrNo = this.strNo;
6: p.AddStr.AddressNo = this.addStr.AddressNo;
7: p.AddStr.AddressStr = this.addStr.AddressStr;
8:
9: return p;
10: }
前台调用如下:
1: //使用深克隆的情况
2: People peopleLi = new People();
3: peopleLi = (People)people.Clone();
4: //peopleLi = people.DeepClone();
5: //peopleLi = people.DeepCloneBySerialize();
6: Console.WriteLine("情况3(使用深克隆的情况)原始值是:" + peopleLi.ToString());
7: peopleLi.StrNo = 4;
8: peopleLi.AddStr.AddressNo = "8888888";
9: Console.WriteLine("情况3(使用深克隆的情况)修改后peopleLi的值是:" + peopleLi.ToString());
10: Console.WriteLine("情况3(使用深克隆的情况)修改后people的值是:" + people.ToString());
11: Console.ReadLine();
输出结果是:

通过上面的实现代码,有下面两个问题:
5、问题一:如果类里面嵌套有多个类,然后嵌套类里面又嵌套类,那么像上面实现深拷贝的方法还能用吗?
解决办法:可以通过序列化的形式来实现深拷贝,这样不管嵌套多少对象,我们不用关注具体的实现,只需要序列化后再反序列化就行了。代码如下:
1: public People DeepCloneBySerialize()
2: {
3: //调用此方法,必须把类标记为可序列化的,嵌套类也需要标记为可序列化的
4: using (Stream objectStream = new MemoryStream())
5: {
6: IFormatter formatter = new BinaryFormatter();
7: formatter.Serialize(objectStream, this);
8: objectStream.Seek(0, SeekOrigin.Begin);
9: return formatter.Deserialize(objectStream) as People;
10: }
11: }
6、问题二:实现深拷贝时,一定要实现ICloneable接口吗?
答案:不一定,ICloneable接口是微软提供的一个对象复制的接口,但是它并没有规定是深拷贝还是浅拷贝,由用户自己来决定实现哪一种的拷贝。也可以完全写一个方法实现深拷贝,不用实现这个接口。如下面的代码:
1: public People DeepClone()
2: {
3: People p = new People();
4: p.StrName = this.strName;
5: p.StrNo = this.strNo;
6: p.AddStr.AddressNo = this.addStr.AddressNo;
7: p.AddStr.AddressStr = this.addStr.AddressStr;
8:
9: return p;
10: }
这样就觉得ICloneable接口没有啥作用,而且还有一点不好的就是,如果父类实现了ICloneable接口,那么子类就必须要实现这个接口,因为如果子类不实现这个接口,那么调用子类的这个方法时,它就会去调用父类的这个方法,而父类的这个方法返回的是对父类这个对象 的拷贝,跟子类对象不是一个概念,所以就会有问题。
参考资料:
http://www.cnblogs.com/luminji/archive/2011/02/02/1948826.html
.NET基础知识之八——深拷贝,浅拷贝的更多相关文章
- Java基础 深拷贝浅拷贝
Java基础 深拷贝浅拷贝 非基本数据类型 需要new新空间 class Student implements Cloneable{ private int id; private String na ...
- Python开发【第二篇】:Python基础知识
Python基础知识 一.初识基本数据类型 类型: int(整型) 在32位机器上,整数的位数为32位,取值范围为-2**31-2**31-1,即-2147483648-2147483647 在64位 ...
- 浅析C++基础知识
近期想对C++的面试题目进行一下更加详细的整理.事实上认真思考一下C++程序猿的面试,我们能够发现对程序猿的能力的考察总是万变不离当中,这些基础知识主要分为五部分:一. C/C++基础知识 二. C/ ...
- JVM菜鸟进阶高手之路十(基础知识开场白)
转载请注明原创出处,谢谢! 最近没有什么实战,准备把JVM知识梳理一遍,先以开发人员的交流来谈谈jvm这块的知识以及重要性,依稀记得2.3年前用solr的时候老是经常oom,提到oom大家应该都不陌生 ...
- 面试基础知识集合(python、计算机网络、操作系统、数据结构、数据库等杂记)
python python _.__.__xx__之间的差别 python中range.xrange和randrange的区别 python中 =.copy.deepcopy的差别 python 继承 ...
- C++之基础知识20170830
/*************************************************************************************************** ...
- 【前端】之JavaScript基础知识
JS 基础知识 JS中,简单类型的数据存储在栈中,复杂类型的数据存储在堆中,其引用存储在栈中 JS中的深拷贝和浅拷贝: 浅拷贝:将对象中的所有简单类型的属性拷贝出来,引用类型属性直接赋值null 深拷 ...
- python基础知识的学习和理解
参考链接:https://github.com/yanhualei/about_python/tree/master/python_learning/python_base python基础知识笔 ...
- 「Java面试题/知识点精华集」20000+字的Java基础知识篇(2020最新版) !
本文已经收录进我的 79K Star 的 Java 开源项目 JavaGuide:https://github.com/Snailclimb/JavaGuide (「Java学习+面试指南」一份涵盖大 ...
随机推荐
- BZOJ1951:[SDOI2010]古代猪文(Lucas,CRT)
Description “在那山的那边海的那边有一群小肥猪.他们活泼又聪明,他们调皮又灵敏.他们自由自在生活在那绿色的大草坪,他们善良勇敢相互都关心……” ——选自猪王国民歌 很久很久以前,在山的那边 ...
- 2018.10.10 Java的The superclass "javax.servlet.http.HttpServlet" was not found on the Java Build Path 错误
我们在用Eclipse进行Java web开发时,可能会出现这样的错误:The superclass javax.servlet.http.HttpServlet was not found on t ...
- File,FileInfo,Directory,DirectoryInfo
两者的共同点: 一:都用于典型的操作,如复制.移动.重命名.创建.打开.删除和追加到文件 二:默认情况下,将向所有用户授予对新文件的完全读/写访问权限. 两者的区别: File类是静态类, ...
- 【luogu P3469 [POI2008]BLO-Blockade】 题解
题目链接:https://www.luogu.org/problemnew/show/P3469 #include <cstdio> #include <cstring> #i ...
- android jni 之C语言基础
*含义 1.乘法 3*5 2.定义指针变量 int * p://定义了一个名字叫p的变量,能够存放int数据类型的地址 3.指针运算符, //如果p是一个已经定义好的指针变量则*p表示以p的内容为地址 ...
- JavaScript中的Map和Set
JavaScript的默认对象表示方法{}可以视为其他语言中的Map或者Dictionary的数据结构,即一组键值对. 但是JavaScript的对象有个小问题,就是键必须是字符串,但实际上Numbe ...
- BionicApi 学习笔记
1.内存管理 malloc, realloc, free new, delete2.文件输入操作 fopen, fwrite, fputs, fputc, fprintf, fflush fread, ...
- SQL里的concat() 以及group_concat() 函数的使用
实例参考:https://blog.csdn.net/mary19920410/article/details/76545053 一 concat()函数 1.功能:将多个字符串连接成一个字符串. 2 ...
- rest_framework--序列化组件
#####序列化组件##### 一.什么是序列化 在python中一切皆对象,把对象从内存中变成可存储或传输的过程称之为序列化. 你写一个django项目肯定是有前后端交互的吧,虽然django也可以 ...
- 取消Eclipse的自动代码格式化
前段时间在Eclipse里面设置了java文件保存时自动格式化,在java->Code Style->Formatter里设置了自定义的格式化的样式,这样每次保存后都会自动格式化代码,用了 ...