.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学习+面试指南」一份涵盖大 ...
随机推荐
- gluon 实现多层感知机MLP分类FashionMNIST
from mxnet import gluon,init from mxnet.gluon import loss as gloss, nn from mxnet.gluon import data ...
- next_permutation,POJ(1256)
题目链接:http://poj.org/problem?id=1256 解题报告: 1.sort函数是按照ASC11码排序,而这里是按照 'A'<'a'<'B'<'b'<... ...
- 【洛谷P1037】 产生数
产生数 题目链接 本着“水题不可大做”的原则,我直接字符串hash+爆搜,成功爆栈.. 我们发现,依次搜索每一位能取到的数字个数,最后乘起来即可(乘法原理) 然后又爆了一个点.. long long存 ...
- 【洛谷P1073】[NOIP2009]最优贸易
最优贸易 题目链接 看题解后感觉分层图好像非常NB巧妙 建三层n个点的图,每层图对应的边相连,权值为0 即从一个城市到另一个城市,不进行交易的收益为0 第一层的点连向第二层对应的点的边权为-w[i], ...
- 【luogu P3371 单源最短路径 】 模板 SPFA优化
无优化:500ms deque优化:400ms #include <queue> #include <cstdio> #include <cstring> #inc ...
- Android学习笔记_49_Android中自定义属性(attrs.xml,TypedArray的使用)
做Android布局是件很享受的事,这得益于他良好的xml方式.使用xml可以快速有效的为软件定义界面.可是有时候我们总感觉官方定义的一些基本组件不够用,自定义组件就不可避免了.那么如何才能做到像官方 ...
- MFC通过URL下载并保存文件代码 转载
http://blog.csdn.net/charlessimonyi/article/details/8666108?utm_source=tuicool&utm_medium=referr ...
- C#基础实例
using System;using System.Collections.Generic;using System.Linq;using System.Web;using System.Web.UI ...
- 搭建基本的React Native开发环境
步骤如下: 1.安装HomeBrew,命令如下: 在终端输入命令:$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Home ...
- lua浮点数取整
向下取整 math.floor(num) 向上取整 math.ceil(num) 取整取余 math.modf(num) 测试 num = 12.4 print(math.floor(num)) 12 ...