.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学习+面试指南」一份涵盖大 ...
随机推荐
- POJ-3267 The Cow Lexicon---删除字符匹配单词
题目链接: https://cn.vjudge.net/problem/POJ-3267 题目大意: 题意就是给出一个主串,和一本字典,问最少在主串删除多少字母,可以使其匹配到字典的单词序列. PS: ...
- 云盘+Git GUI实现云盘文件版本号控制
以下介绍操作细节 1.先下载Git GUI 下载地址:http://msysgit.github.io/ 再下载百度云网盘 下载地址:http://pan.baidu.com 接下来就是安 ...
- [JSOI2010]部落划分
嘟嘟嘟 一道不错的题,解法不少. 最易于理解的是最小生成树的做法: 首先每两个点之间都连一条长度为这两个点的距离的边,形成完全图. 然后跑最小生成树,直到剩k个联通块,这时候合并成k - 1个联通块的 ...
- [USACO09FEB] Revamping Trails 【分层图+Dijkstra】
任意门:https://www.luogu.org/problemnew/show/P2939 Revamping Trails 题目描述 Farmer John dutifully checks o ...
- java读取资源文件(Properties)
四步: java代码 //new一个读取配置文件 Properties properties=new Properties(); //获取文件路径 String path=request.getSer ...
- notepad++括号自动补全插件: XBracket Lite
1.4.5.1. 通过XBracket Lite实现括号的自动补全 先去打开相应的设置: 再根据自己的需要去设置: 其中解释一下相应的选项的含义: Treat'' as brackets 把单引号', ...
- LeetCode12.整数转罗马数字 JavaScript
罗马数字包含以下七种字符: I, V, X, L,C,D 和 M. 字符 数值 I 1 V 5 X 10 L 50 C 100 D 500 M 1000 例如, 罗马数字 2 写做 II ,即为两个并 ...
- Java基础——数组复习
数组是一个变量,存储相同数据类型的一组数据 声明一个变量就是在内存空间划出一块合适的空间 声明一个数组就是在内存空间划出一串连续的空间 数组长度固定不变,避免数组越界 数组是静态分配内存空间的,所 ...
- rest_framework--RESTful规范
#####RESTful规范##### 一.什么是restful restful其实就是一种软件架构风格,跟技术毫无关系.是一种面向资源编程的方法. 说起面向资源编程,我想起了之前了解到的面向过程编程 ...
- 【HDOJ 1272】小希的迷宫(并查集+无环图)
描述 上次Gardon的迷宫城堡小希玩了很久(见Problem B),现在她也想设计一个迷宫让Gardon来走.但是她设计迷宫的思路不一样,首先她认为所有的通道都应该是双向连通的,就是说如果有一个通道 ...