[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 区别:基本类型效率更高,类 ...
随机推荐
- 读到的"关于授权"
关于授权 记得公司刚有十几个人的时候,全公司我最忙,经常同时接两三个销售电话,还得安排送货,结账,进货,每天来得最早,走得最晚.一次我弟弟到公司,看了半天,发表感慨说:"哥我怎么觉得你在养活 ...
- Solr -- 查询语法/参数
1. 常用查询参数 参数 描述 defType 指定用于处理查询语句(参数q的内容)的查询解析器,eg:defType=lucene sort 指定响应的排序方式:升序asc或降序desc.同时需要指 ...
- django-模板,过滤器
for…in…:跟python中的for…in…是一样的用法 {% for m in modules %} {{ forloop.cpunter }} {{ m }} {% end %} forloo ...
- OpenCL 使用函数 clCreateProgramWithBinary 来创建程序
▶ 函数 clCreateProgramWithSource 接收 OpenCL 代码(设备无关)来创建程序,而函数 clCreateProgramWithBinary 接收已经经过函数 clBuil ...
- IOS调试技巧:当程序崩溃的时候怎么办 xcode调试
转自:http://www.ityran.com/archives/1143 ------------------------------------------------ 欢迎回到当程序崩溃的时候 ...
- clr相关名词
程序集:一个或多个类型定义文件和资源文件的集合 Native Code(本机代码): 已被编译为特定于处理器的机器码的代码. 本地代码(native code)是计算机编程(代码),编译用来运行一个特 ...
- Multithread之为什么spinlock必须是volatile?
[Multithread之为什么spinlock必须是volatile?] 1.编译器的优化 在本次线程内,当读取一个变量时,为提高存取速度,编译器优化时有时会先把变量读取到一个寄存器中:以后再取变量 ...
- 给我们的Empty Object加个图标
Gizmos.DrawIcon (transform.position, "1.png", true);
- Greeplum 系列(一) Greenplum 架构
Greeplum 系列(一) Greenplum 架构 Greenplum 可进行海量并行处理 (Massively Parallel Processing) 一.Greenplum 体系架构 Gre ...
- mongo学习- 副本集 大多数原则
副本集中有一个重要的概念“大多数”,意思是说,选择主节点需要大多数决定(本人亲自做了实验) 步骤: 1.开启副本集(如果没有配置好 副本集的 亲参考我的上篇文章 https://www.cnblog ...