.NET面试题解析(02)-拆箱与装箱
装箱和拆箱几乎是所有面试题中必考之一,看上去简单,就往往容易被忽视。其实它一点都不简单的,一个简单的问题也可以从多个层次来解读。
常见面试题目:
1.什么是拆箱和装箱?
2.什么是箱子?
3.箱子放在哪里?
4.装箱和拆箱有什么性能影响?
5.如何避免隐身装箱?
6.箱子的基本结构?
7.装箱的过程?
8.拆箱的过程?
9.下面这段代码输出什么?共发生多少次装箱?多少次拆箱?
int i = ;
object obj = i;
IFormattable ftt = i;
Console.WriteLine(System.Object.ReferenceEquals(i, obj));
Console.WriteLine(System.Object.ReferenceEquals(i, ftt));
Console.WriteLine(System.Object.ReferenceEquals(ftt, obj));
Console.WriteLine(System.Object.ReferenceEquals(i, (int)obj));
Console.WriteLine(System.Object.ReferenceEquals(i, (int)ftt));
深入浅出装箱与拆箱
有拆必有装,有装必有拆。
在上一文中我们提到,所有值类型都是继承自System.ValueType,而System.ValueType又是来自何方呢,不难发现System.ValueType继承自System.Object。因此Object是.NET中的万物之源,几乎所有类型都来自她,这是装箱与拆箱的基础。
特别注意的是,本文与上一文有直接关联,需要先了解上一文中值类型与引用类型的原理,才可以更好理解本文的内容。
基本概念
拆箱与装箱就是值类型与引用类型的转换,她是值类型和引用类型之间的桥梁,他们可以相互转换的一个基本前提就是上面所说的:Object是.NET中的万物之源
先看看一个小小的实例代码:
int x = ;
object o = x; //装箱
int y = (int) o; //拆箱
装箱:值类型转换为引用对象,一般是转换为System.Object类型或值类型实现的接口引用类型;
拆箱:引用类型转换为值类型,注意,这里的引用类型只能是被装箱的引用类型对象;
由于值类型和引用类型在内存分配的不同,从内存执行角度看,拆箱与装箱就势必存在内存的分配与数据的拷贝等操作,这也是装箱与拆箱性能影响的根源。
装箱的过程
int x = ;
object o = x; //装箱
装箱就是把值类型转换为引用类型,具体过程:
- 1.在堆中申请内存,内存大小为值类型的大小,再加上额外固定空间(引用类型的标配:TypeHandle和同步索引块);
- 2.将值类型的字段值(x=1023)拷贝新分配的内存中;
- 3.返回新引用对象的地址(给引用变量object o)
如上图所示,装箱后内存有两个对象:一个是值类型变量x,另一个就是新引用对象o。装箱对应的IL指令为box
,上面装箱的IL代码如下图:
拆箱的过程
int x = ;
object o = x; //装箱
int y = (int) o; //拆箱
明白了装箱,拆箱就是装箱相反的过程,简单的说是把装箱后的引用类型转换为值类型。具体过程:
- 1.检查实例对象(object o)是否有效,如是否为null,其装箱的类型与拆箱的类型(int)是否一致,如检测不合法,抛出异常;
- 2.指针返回,就是获取装箱对象(object o)中值类型字段值的地址;
- 3.字段拷贝,把装箱对象(object o)中值类型字段值拷贝到栈上,意思就是创建一个新的值类型变量来存储拆箱后的值;
如上图所示,拆箱后,得到一个新的值类型变量y,拆箱对应的IL指令为unbox
,拆箱的IL代码如下:
装箱与拆箱总结及性能
装的的什么?拆的又是什么?什么是箱子?
通过上面深入了解了装箱与拆箱的原理,不难理解,只有值类型可以装箱,拆的就是装箱后的引用对象,箱子就是一个存放了值类型字段的引用对象实例,箱子存储在托管堆上。只有值类型才有装箱、拆箱两个状态,而引用类型一直都在箱子里。
关于性能
之所以关注装箱与拆箱,主要原因就是他们的性能问题,而且在日常编码中,经常有装箱与拆箱的操作,而且这些装箱与拆箱的操作往往是在不经意时发生。一般来说,装箱的性能开销更大,这不难理解,因为引用对象的分配更加复杂,成本也更高,值类型分配在栈上,分配和释放的效率都很高。装箱过程是需要创建一个新的引用类型对象实例,拆箱过程需要创建一个值类型字段,开销更低。
为了尽量避免这种性能损失,尽量使用泛型,在代码编写中也尽量避免隐式装箱。
什么是隐式装箱?如何避免?
就是不经意的代码导致多次重复的装箱操作,看看代码就好理解了
int x = ;
ArrayList arr = new ArrayList();
arr.Add(x);
arr.Add(x);
arr.Add(x);
这段代码共有多少次装箱呢?看看Add方法的定义:
再看看IL代码,可以准确的得到装箱的次数:
显示装箱可以避免隐式装箱,下面修改后的代码就只有一次装箱了。
int x = ;
ArrayList arr = new ArrayList();
object o = x;
arr.Add(o);
arr.Add(o);
arr.Add(o);
题目答案解析:
1.什么是拆箱和装箱?
装箱就是值类型转换为引用类型,拆箱就是引用类型(被装箱的对象)转换为值类型。
2.什么是箱子?
就是引用类型对象。
3.箱子放在哪里?
托管堆上。
4.装箱和拆箱有什么性能影响?
装箱和拆箱都涉及到内存的分配和对象的创建,有较大的性能影响。
5.如何避免隐身装箱?
编码中,多使用泛型、显示装箱。
6.箱子的基本结构?
上面说了,箱子就是一个引用类型对象,因此她的结构,主要包含两部分:
- 值类型字段值;
- 引用类型的标准配置,引用对象的额外空间:TypeHandle和同步索引块,关于这两个概念在本系列后面的文章会深入探讨。
7.装箱的过程?
- 1.在堆中申请内存,内存大小为值类型的大小,再加上额外固定空间(引用类型的标配:TypeHandle和同步索引块);
- 2.将值类型的字段值(x=1023)拷贝新分配的内存中;
- 3.返回新引用对象的地址(给引用变量object o)
8.拆箱的过程?
- 1.检查实例对象(object o)是否有效,如是否为null,其装箱的类型与拆箱的类型(int)是否一致,如检测不合法,抛出异常;
- 2.指针返回,就是获取装箱对象(object o)中值类型字段值的地址;
- 3.字段拷贝,把装箱对象(object o)中值类型字段值拷贝到栈上,意思就是创建一个新的值类型变量来存储拆箱后的值;
9.下面这段代码输出什么?共发生多少次装箱?多少次拆箱?
int i = ;
object obj = i;
IFormattable ftt = i;
Console.WriteLine(System.Object.ReferenceEquals(i, obj));
Console.WriteLine(System.Object.ReferenceEquals(i, ftt));
Console.WriteLine(System.Object.ReferenceEquals(ftt, obj));
Console.WriteLine(System.Object.ReferenceEquals(i, (int)obj));
Console.WriteLine(System.Object.ReferenceEquals(i, (int)ftt));
上面代码输出如下,至于发生多少次装箱多少次拆箱,你猜?
False
False
False
False
False
版权所有,文章来源:http://www.cnblogs.com/anding
个人能力有限,本文内容仅供学习、探讨,欢迎指正、交流。
.NET面试题解析(00)-开篇来谈谈面试 & 系列文章索引
参考资料:
书籍:CLR via C#
书籍:你必须知道的.NET
1.4.2 装箱和拆箱:http://book.51cto.com/art/201012/237726.htm
.NET面试题解析(02)-拆箱与装箱的更多相关文章
- .NET基础知识(02)-拆箱与装箱
装箱和拆箱几乎是所有面试题中必考之一,看上去简单,就往往容易被忽视.其实它一点都不简单的,一个简单的问题也可以从多个层次来解读. 常见面试题目: 1.什么是拆箱和装箱? 2.什么是箱子? 3.箱子放在 ...
- JAVA进阶之旅(一)——增强for循环,基本数据类型的自动拆箱与装箱,享元设计模式,枚举的概述,枚举的应用,枚举的构造方法,枚举的抽象方法
JAVA进阶之旅(一)--增强for循环,基本数据类型的自动拆箱与装箱,享元设计模式,枚举的概述,枚举的应用,枚举的构造方法,枚举的抽象方法 学完我们的java之旅,其实收获还是很多的,但是依然还有很 ...
- Java知多少(24)包装类、拆箱和装箱详解
虽然 Java 语言是典型的面向对象编程语言,但其中的八种基本数据类型并不支持面向对象编程,基本类型的数据不具备“对象”的特性——不携带属性.没有方法可调用. 沿用它们只是为了迎合人类根深蒂固的习惯, ...
- JAVA中拆箱和装箱
浅谈JAVA中拆箱与装箱 一. 什么是装箱?什么是拆箱? 在Java SE5之前,如果要生成一个数值为10的Integer对象,必须这样进行: Integer i = new Integer(10) ...
- C# 拆箱与装箱及优化
1.概念 装箱在值类型向引用类型转换时发生,在堆中分配. 拆箱在引用类型向值类型转换时发生. 2.装箱拆箱的过程 //这行语句将整型常量1赋给object类型的变量obj:众所周知常量1是值类型,值类 ...
- [Java学习] Java包装类、拆箱和装箱详解
虽然 Java 语言是典型的面向对象编程语言,但其中的八种基本数据类型并不支持面向对象编程,基本类型的数据不具备“对象”的特性——不携带属性.没有方法可调用. 沿用它们只是为了迎合人类根深蒂固的习惯, ...
- Java包装类、拆箱和装箱详解
转载:https://www.cnblogs.com/ok932343846/p/6749488.html 虽然 Java 语言是典型的面向对象编程语言,但其中的八种基本数据类型并不支持面向对象编程, ...
- int和Integer及拆箱与装箱
int和Integer 如果面试官问Integer与int的区别:估计大多数人只会说道两点,Ingeter是int的包装类,int的初值为0,Ingeter的初值为null.但是如果面试官再问一下In ...
- 《Java基础知识》Java包装类,拆箱和装箱
虽然 Java 语言是典型的面向对象编程语言,但其中的八种基本数据类型并不支持面向对象编程,基本类型的数据不具备“对象”的特性——不携带属性.没有方法可调用. 沿用它们只是为了迎合人类根深蒂固的习惯, ...
随机推荐
- myeclipse使用maven插件进行maven install时报错check $m2_home environment variable and mvn script match
check $m2_home environment variable and mvn script match 分类: maven2015-09-01 18:06 842人阅读 评论(0) 收藏 举 ...
- TortoiseSVN客户端使用说明
TortoiseSVN客户端使用说明 TortoiseSVN不是一个独立的窗口程序,而是集成在windows右键菜单中,下面对TortoiseSVN的菜单项做简要的说明.01.SVN Checkout ...
- Python学习之路--面向对象
1.面向对象概述 面向过程:根据业务逻辑从上到下写垒代码 函数式:将某功能代码封装到函数中,日后便无需重复编写,仅调用函数即可 面向对象:对函数进行分类和封装,让开发“更快更好更强...” 面向 ...
- make基础(转)
1. 基本规则 请点评 除了Hello World这种极简单的程序之外,一般的程序都是由多个源文件编译链接而成的,这些源文件的处理步骤通常用Makefile来管理.Makefile起什么作用呢?我们先 ...
- PHPExcel(1.8.0) 帮助代码
以下代码不能直接运行,只是提供各种方法调用写法. require_once dirname(__FILE__) . '/Classes/PHPExcel.php'; $objPHPExcel = ne ...
- android wifi ANR问题分析总结
android wifi ANR问题分析总结 1 看看main进程阻塞在那里? 2 调用关系的函数阻塞在那里? 3 最终阻塞函数的阻塞前的log以及状态
- 前台传参数时间类型不匹配:type 'java.lang.String' to required type 'java.util.Date' for property 'createDate'
springMVC action接收参数: org.springframework.validation.BindException: org.springframework.validation.B ...
- [poi2010]Hamsters
题意:Tz养了一群仓鼠,他们都有英文小写的名字,现在Tz想用一个字母序列来表示他们的名字,只要他们的名字是字母序列中的一个子串就算,出现多次可以重复计算.现在Tz想好了要出现多少个名字,请你求出最短的 ...
- 你好,欢迎来到我的博客,我是博主royalmice1
你好,欢迎来到我的博客,我是博主royalmice
- Linux内核--网络栈实现分析(五)--传输层之UDP协议(上)
本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明出处http://blog.csdn.net/yming0221/article/details/7532512 更多请看专栏, ...