[C#学习笔记]你真的理解拆箱装箱吗?
学习一项新知识的时候,最好的方法就是去实践它。
前言
《CLR via C#》这本神书真的是太有意思了!没错我的前言就是这个。
装箱
首先来看下,下面这段代码
可以看到,每次循环迭代都会初始化一个Point的值类型字段,并将该Point存储到ArrayList中。
但是我们肯定有疑问,ArrayList中究竟存储了什么?是Point结构,Point结构的地址,还是其他完全不同的东西?
为了解答这个问题,我们必须研究一下ArrayList的Add方法,了解它的参数被定义成了什么类型。
可以知道,Add的原型如下:
public virtual Int32 Add(Object value);
说明Add获取的是一个Object参数。也就是说,Add获取对托管对上的一个对象的引用(或指针)来作为参数。但是之前的代码传递的是p,也就是一个Point,是一个值类型。
为了使代码能够正确的工作,Point值类型必须转换成真正的、在堆中托管的对象,而且必须获取对该对象的引用。
那么,这个将值类型转换成引用类型的操作,就叫做装箱。
当值类型的实力进行装箱时,发生了以下三件事情:
1、在托管堆中分配内存。分配的内存量是值类型各个字段所需要的内存量,还要加上托管堆所有对象都有的两个额外成员(类型对象指针和同步块索引)所需要的内存量。关于类型对象指针和同步块索引,可以看我的这边博文:https://www.cnblogs.com/knqiufan/p/10475186.html
2、值类型的字段复制到新分配的堆内存。
3、返回对象地址。现在该地址是对象引用;值类型成了引用类型。
C#编译器自动生成堆值类型实力进行装箱所需的IL代码。
所以在运行时,当前存在于Point值类型实力p中的字段复制到新分配的Point对象中。已经装箱的Point对象(现在是引用类型)的地址返回并传给Add方法。Point对象一直存在于堆中,直至被垃圾回收。
(所以之所以说相比于ArrayList,尽量用泛型List<T>,因为泛型集合List<T>允许开发人员在操作值类型的集合时不需要对集合中的项进行装箱或拆箱操作。这一改进,使性能提高了不少,因为托管堆中需要创建的对象减少了,进而减少了应用程序需要执行的垃圾回收的次数。)
拆箱
说完了装箱,现在来说说拆箱。假定我继续执行以下操作:
它获取ArrayList的元素0包含的引用(或指针),试图将其放到Point值类型的实力p2中。
为此,已经装箱的Point对象中的所有字段都必须复制到值类型变量p2中,后者在线程栈上。
CLR分为两步完成复制:
1、获取已经装箱Point对象中的各个Point字段的地址。这个过程就是拆箱。
2、将字段包含的值从堆复制到基于栈的值类型的实例中。
拆箱并不是直接将装箱过程倒过来,拆箱的代价要低的多。拆箱其实就是获取指针的过程,该指针指向包含在一个对象中的原始值类型(数据字段)。
其实指针指向的是已经装箱实例中的未装箱部分。所以和装箱不同,拆箱不要求在内存中复制任何字节。
已经装箱的值类型实例在进行拆箱时,内部发生了下面这些事情:
1、如果包含“对已经装箱值类型实例的引用”的变量为null,则抛出NullReferenceException异常。
2、如果引用的对象不是所需值类型的已装箱实例,抛出InvalidCastException异常。
[C#学习笔记]你真的理解拆箱装箱吗?的更多相关文章
- Java四种引用--《深入理解Java虚拟机》学习笔记及个人理解(四)
Java四种引用--<深入理解Java虚拟机>学习笔记及个人理解(四) 书上P65. StrongReference(强引用) 类似Object obj = new Object() 这类 ...
- Java虚拟机内存溢出异常--《深入理解Java虚拟机》学习笔记及个人理解(三)
Java虚拟机内存溢出异常--<深入理解Java虚拟机>学习笔记及个人理解(三) 书上P39 1. 堆内存溢出 不断地创建对象, 而且保证创建的这些对象不会被回收即可(让GC Root可达 ...
- Java 从Character和char的区别来学习自动拆箱装箱
本文结构 1.Character和char 的区别: 2.自动拆箱装箱 1.Character和char 的区别: Character是类,char基本数据类型. 在java中有三个类负责对字符的操作 ...
- WPF中多线程统计拆箱装箱和泛型的运行效率
WPF中多线程统计拆箱装箱和泛型的执行效率.使用的知识点有泛型.多线程.托付.从样例中能够看到使用泛型的效率至少提升2倍 MainWindow.xaml <Window x:Class=&quo ...
- [ 转载 ]学习笔记-深入剖析Java中的装箱和拆箱
深入剖析Java中的装箱和拆箱 自动装箱和拆箱问题是Java中一个老生常谈的问题了,今天我们就来一些看一下装箱和拆箱中的若干问题.本文先讲述装箱和拆箱最基本的东西,再来看一下面试笔试中经常遇到的与装箱 ...
- 【学习笔记】深入理解js原型和闭包(0)——目录
文章转载:https://www.cnblogs.com/wangfupeng1988/p/4001284.html 说明: 本篇文章一共16篇章,外加两篇后补的和一篇自己后来添加的学习笔记,一共19 ...
- 【疯狂Java学习笔记】【理解面向对象】
[学习笔记]1.Java语言是纯粹的面向对象语言,这体现在Java完全支持面向对象的三大基本特征:封装.继承.多态.抽象也是面向对象的重要组成部分,不过它不是面向对象的特征之一,因为所有的编程语言都需 ...
- CSS学习笔记09 简单理解BFC
引子 在讲BFC之前,先来看看一个例子 <!DOCTYPE html> <html lang="en"> <head> <meta cha ...
- Java中的自动拆箱装箱(Autoboxing&Unboxing)
一.基本类型打包器 1.基本类型:long.int.double.float.boolean 2.类类型:Long.Integer.Double.Float.Boolean 区别:基本类型效率更高,类 ...
随机推荐
- ThreadCachedInt
folly/ThreadCachedInt.h High-performance atomic increment using thread caching. folly/ThreadCachedIn ...
- Carrying per-request context using the HttpRequestMessage.Properties
In a Web API application, I use Castle Windsor to supply services configured with PerWebRequest life ...
- 国外接活网站Elance, Freelancer和ScriptLance的介绍和对比
国外接活网站Elance, Freelancer和ScriptLance的介绍和对比littleben 一年以前 (via WEB)http://www.geekpark.net/entity/vie ...
- Rhythmk 一步一步学 JAVA (18): Enum枚举学习
枚举定义: public enum SizeEnum { SMALL, BIG, BIGEST }; public enum SizeStringEnum { SMALL("小") ...
- random.nextint()
自从JDK最初版本发布起,我们就可以使用java.util.Random类产生随机数了.在JDK1.2中,Random类有了一个名为nextInt()的方法: public int nextInt(i ...
- js 实现数组深度copy
1. slice() slice() 方法可从已有的数组中返回选定的元素.arrayObject.slice(start,end) ,返回一个新的数组,包含从 start 到 end (不包括该元素) ...
- gorm中自己写sql的方法实现
type Result struct { Total int } var result Result //当天修改作业的总时间:分钟 dao.DB(dao.HomeworkTable).Raw(&qu ...
- react native 触摸Touchable***的区别(TouchableWithoutFeedback、TouchableOpacity、TouchableHighlight、TouchableNativeFeedback)
一.问题背景: react native的跨平台开发没有button的概念,而是使用touchable系列实现点击触发效果. 而touchable系列就有四个之多,而且相互之间仍有较大差别,这就给我们 ...
- Linux文件权限查看及修改命令chmod,chown
查看权限 Linux文件访问权限分为可读,可写和可执行三种. 可用ls -l命令查看,例: ls -l或者 ll 显示为 -r--r--r--. 1 root root 21 Jan 5 23:02 ...
- 32-python代码打包成exe文件-pyinstaller
安装 pyinstaller: pip install pyinstall 简单使用 最简单的使用方式是运行 pyinstaller myscript.py 来生成可执行文件,其中 myscript. ...