C#浅拷贝和深拷贝实例解析
作者:shichen2014 发布时间:2022-09-03 02:31:05
在有些时候,我们需要从数据库读取数据填充对象或从硬盘读取文件填充对象,但是这样做相对耗时。这时候我们就想到了对象的拷贝。本文即以实例形式解析了C#浅拷贝和深拷贝的用法。具体如下:
一、浅拷贝
1.什么是"浅拷贝":
当针对一个对象前拷贝的时候,对于对象的值类型成员,会复制其本身,对于对象的引用类型成员,仅仅复制对象引用,这个引用指向托管堆上的对象实例。
2.有一个对象,包含引用类型的类成员和值类型的struct成员
Cinema包含引用类型成员Room和值类型成员Film。
public class Room
{
public int _maxSeat;
public Room(int maxSeat)
{
this._maxSeat = maxSeat;
}
}
public struct Film
{
public string _name;
public Film(string name)
{
this._name = name;
}
}
public class Cinema
{
public Room _room;
public Film _film;
public Cinema(Room room, Film film)
{
this._room = room;
this._film = film;
}
public object Clone()
{
return MemberwiseClone(); //对引用类型实施浅复制
}
}
3.测试拷贝后的效果
①打印出原先对象拷贝前值类型和引用类型成员的值
②对原先对象拷贝,打印出复制对象值类型和引用类型成员的值
③改变原先对象的值,再次打印原先对象的值类型和引用类型成员的值
④再次打印复制对象值类型和引用类型成员的值
static void Main(string[] args)
{
Room room1 = new Room(60);
Film film1 = new Film("家园防线");
Cinema cinema1 = new Cinema(room1, film1);
Cinema cinema2 = (Cinema)cinema1.Clone();
Console.WriteLine("拷贝之前,结构成员的字段值为{0},引用类型成员的字段值为{1}", cinema1._film._name,cinema1._room._maxSeat);
Console.WriteLine("拷贝之后,新的结构成员的字段值为{0},引用类型成员的字段值为{1}", cinema2._film._name, cinema2._room._maxSeat);
//修改拷贝之前引用类型的字段值
cinema1._film._name = "极品飞车";
cinema1._room._maxSeat = 80;
Console.WriteLine("修改之后,结构成员的字段值为{0},引用类型成员的字段值为{1}", cinema1._film._name, cinema1._room._maxSeat);
Console.WriteLine("修改之后,新的结构成员的字段值为{0},引用类型成员的字段值为{1}", cinema2._film._name, cinema2._room._maxSeat);
Console.ReadKey();
}
运行结果如下:
分析:
浅拷贝关键点是对引用类型拷贝的是对象引用,这个引用指向托管堆上的对象实例。改变原对应引用类型的值,会影响到复制对象。
二、深拷贝
1.什么是"深拷贝"
对引用成员指向的对象也进行复制,在托管堆上赋值原先对象实例所包含的数据,再在托管堆上创建新的对象实例。
2.通过对每个对象成员进行复制进行深拷贝
public object Clone()
{
Room room = new Room();
room._maxSeat = this._room._maxSeat;//复制当前引用类型成员的值到新对象
Film film = this._film; //值类型直接赋值
Cinema cinema = new Cinema(room, film);
return cinema;
}
3.也可以通过序列化和反序列化进行深拷贝
public object Clone1()
{
BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream();
bf.Serialize(ms, this); //复制到流中
ms.Position = 0;
return (bf.Deserialize(ms));
}
4.采用序列化和反序列化深拷贝,但必须把所有的类打上[Serializable],测试代码如下:
[Serializable]
public class Room
{
public int _maxSeat;
public Room()
{}
public Room(int maxSeat)
{
this._maxSeat = maxSeat;
}
}
[Serializable]
public struct Film
{
public string _name;
public Film(string name)
{
this._name = name;
}
}
[Serializable]
public class Cinema
{
public Room _room;
public Film _film;
public Cinema(Room room, Film film)
{
this._room = room;
this._film = film;
}
//浅拷贝
//public object Clone()
//{
// return MemberwiseClone(); //对引用类型实施浅复制
//}
//深拷贝 对每个对象成员进行复制
public object Clone()
{
Room room = new Room();
room._maxSeat = this._room._maxSeat;//复制当前引用类型成员的值到新对象
Film film = this._film; //值类型直接赋值
Cinema cinema = new Cinema(room, film);
return cinema;
}
//使用序列化和反序列化进行复制
public object Clone1()
{
BinaryFormatter bf = new BinaryFormatter();
MemoryStream ms = new MemoryStream();
bf.Serialize(ms, this); //复制到流中
ms.Position = 0;
return (bf.Deserialize(ms));
}
}
5.测试拷贝后的效果
①打印出原先对象拷贝前值类型和引用类型成员的值
②对原先对象拷贝,打印出复制对象值类型和引用类型成员的值
③改变原先对象的值,再次打印原先对象的值类型和引用类型成员的值
④再次打印复制对象值类型和引用类型成员的值
static void Main(string[] args)
{
Room room1 = new Room(60);
Film film1 = new Film("家园防线");
Cinema cinema1 = new Cinema(room1, film1);
Cinema cinema2 = (Cinema)cinema1.Clone1();
Console.WriteLine("拷贝之前,结构成员的字段值为{0},引用类型成员的字段值为{1}", cinema1._film._name,cinema1._room._maxSeat);
Console.WriteLine("拷贝之后,新的结构成员的字段值为{0},引用类型成员的字段值为{1}", cinema2._film._name, cinema2._room._maxSeat);
//修改拷贝之前引用类型的字段值
cinema1._film._name = "极品飞车";
cinema1._room._maxSeat = 80;
Console.WriteLine("修改之后,结构成员的字段值为{0},引用类型成员的字段值为{1}", cinema1._film._name, cinema1._room._maxSeat);
Console.WriteLine("修改之后,新的结构成员的字段值为{0},引用类型成员的字段值为{1}", cinema2._film._name, cinema2._room._maxSeat);
Console.ReadKey();
}
结果:
分析:
深拷贝后,两个对象的引用成员已经分离,改变原先对象引用类型成员的值并不会对复制对象的引用类型成员值造成影响。


猜你喜欢
- 一、ObjectContext对象上下文Entity SQL 语言 - ADO.NET | Microsoft 官当文档ObjectCont
- springboot返回文件流@GetMapping(value = "/file/{fileName}")public
- 这个问题百度上一搜一大把,基本上都是说找到和SurfaceView的比例相近的camera预览尺寸,但是发现预览时候还是差了点意思,具体看下
- 在我们的程序当中如果要实现类似《360软件管家》的功能,就要解决两个问题,首先是要判断该程序已有一个实例在运行,其次是要将已运行的应用程序实
- 批量添加一对多中间表建立中间表A,一个id对应多个lid;传入两条参数long id;//单个数值List lid;//集合数值dao层语句
- 文章来源:互联网 作者:ggg82/CSDN现在许多用户界面都使用工具栏制作菜单条,小弟最近对此感兴趣,便从网上求助,可是得到的帮助大多是B
- java联调生成测试数据工具类在日常的联调中,我们经常需要准备一定数量的测试数据,用来配合前端测试。当然对于简单的数据类型完全可以通过 JD
- Java 线程对比Thread,Runnable,Callablejava 使用 Thread 类代表线程,所有现场对象都必须是 Threa
- 有人问我,怎么判断一个点是不是在多边形内,本来想着把这个多边形分成一个又一个三角形,如图, 然后判断这个点是不是在某个三角形中,如
- --删除外键 语法:alter table 表名 drop constraint 外键约束名 如: alter table Stu_PkFk
- volatile关键字关于先说它的两个作用:保证变量在内存中对线程的可见性禁用指令重排每个字都认识,凑在一起就麻了这两个作用通常很不容易被我
- 本文实例为大家分享了JSON处理工具类的具体代码,供大家参考,具体内容如下import java.io.IOException; impor
- springboottest测试依赖和使用<dependency> <groupId>or
- 本文实例讲述了C#中datagridview使用tooltip控件显示单元格内容的方法。分享给大家供大家参考,具体如下:代码如下:using
- 1.配置自定义共享线程池(Spring线程池)@Configuration@EnableAsyncpublic class ThreadPo
- 想用java做一个像windows里一样的txt编辑软件,涉及到字体设置选项卡,在网上找了很久都没找到,就生气啦自己写一个,现在贴这里分享一
- 如题,主要使用AsReadOnly这个方法就可以了List<int> a = new List<int> {1, 2
- 上次介绍了位置服务中的Geocoder,这次就来介绍一下LocationManager。LocationManager系统服务是位置服务的核
- 其中第二级目录包含了五个预定义主键分别是:HKEY_CLASSES_ROOT,HKEY_CURRENT_USER,HKEY_LOCAL_MA
- Java 8的18个常用日期处理一、简介伴随lambda表达式、streams以及一系列小优化,Java 8 推出了全新的日期时间API。J