.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学习+面试指南」一份涵盖大 ...
随机推荐
- 使用jvisualvm.exe工具查看java项目内存溢出(堆溢出)
在查看内存溢出的时候,我们需要明白,堆溢出和持久代溢出,他们不一样,说到内存泄漏,我们就需要明白,内存中 年老代和新生代,和持久代,这3块的数据 自己的理解: new了一个对象,会进入到堆里面,先放 ...
- 面向对象编程(OOP)、面向组件编程(COP)、面向方面编程(AOP)和面向服务编程(SOP)
http://blog.csdn.net/hjf19790118/article/details/6919265 1.什么是面向对象编程(Object-Oriented Programming)? 面 ...
- python:常用模块二
1,hashlib模块---摘要算法 import hashlib md5 = hashlib.md5() md5.update('how to use md5 in python hashlib?' ...
- 数字游戏II
题面好难找:嘟嘟嘟 贪心 + dp. 首先要按bi的降序排序,让每一次减少大的数尽量靠前.为啥咧?于是我们就需要证明:令sum = a1 - (1 - 1) * b1 + a2 - (2 - 1) * ...
- Docker 常用指令
1.检查内核版本,必须是3.10及以上uname ‐r2.安装dockeryum install docker3.输入y确认安装4.启动docker[root@localhost ~]# system ...
- ListView与SimpleAdapter
Adapter可以视作控件与数据之间的桥梁 对ListView做自由布局和填充需要使用到Adapter,这里我们采用SimpleAdapter. 简单来说: 1.定义一个ListItem,其数据结构是 ...
- Android学习笔记_6_保存文件到SDCard
一.加入访问sdcard的权限 Environment.getExternalStorageState()方法用于获取SDCard的状态,如果手机装有SDCard,并且可以进行读写,那么方法返回的状态 ...
- U盘安装CentOS7.3教程
0.准备工作: 一台没系统的普通电脑u盘一个(大于1G,最小安装的话不超过1G,根据选择系统大小匹配U盘即可)CentOS7.3 iso文件一个UltraISO工具 1.制作U盘 ①使用UltraIS ...
- java流汇总以及使用实例
流一.基本概念 Java中对文件的操作是以流的方式进行的.流是Java内存中的一组有序数据序列.Java将数据从源(文件.内存.键盘.网络) 读入到内存中,形成了流,然后将这些流还可以写到另外的目的地 ...
- iOS | AFNetworking封装
为大家分享一个IOS处理网络请求,网络上传,网络下载等功能全面的一个第三方框架-AFNetworking,这是一个使用非常方便的网络框架. 最新的版本是基于NSURLSession,原来的NSURLC ...