装箱和拆箱几乎是全部面试题中必考之一,看上去简单,就每每容易被忽视。其实它一点都不简单的,一个简单的问题也能够从多个层次来解读。html
1.什么是拆箱和装箱?面试
2.什么是箱子?性能
3.箱子放在哪里?学习
4.装箱和拆箱有什么性能影响?编码
5.如何避免隐身装箱?spa
6.箱子的基本结构?指针
7.装箱的过程?code
8.拆箱的过程?orm
9.下面这段代码输出什么?共发生多少次装箱?多少次拆箱?htm
int i = 5; 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 = 1023; object o = x; //装箱 int y = (int) o; //拆箱
装箱:值类型转换为引用对象,通常是转换为System.Object类型或值类型实现的接口引用类型;
拆箱:引用类型转换为值类型,注意,这里的引用类型只能是被装箱的引用类型对象;
因为值类型和引用类型在内存分配的不一样,从内存执行角度看,拆箱与装箱就势必存在内存的分配与数据的拷贝等操做,这也是装箱与拆箱性能影响的根源。
int x = 1023; object o = x; //装箱
装箱就是把值类型转换为引用类型,具体过程:
如上图所示,装箱后内存有两个对象:一个是值类型变量x,另外一个就是新引用对象o。装箱对应的IL指令为box
,上面装箱的IL代码以下图:
int x = 1023; object o = x; //装箱 int y = (int) o; //拆箱
明白了装箱,拆箱就是装箱相反的过程,简单的说是把装箱后的引用类型转换为值类型。具体过程:
如上图所示,拆箱后,获得一个新的值类型变量y,拆箱对应的IL指令为unbox
,拆箱的IL代码以下:
经过上面深刻了解了装箱与拆箱的原理,不难理解,只有值类型能够装箱,拆的就是装箱后的引用对象,箱子就是一个存放了值类型字段的引用对象实例,箱子存储在托管堆上。只有值类型才有装箱、拆箱两个状态,而引用类型一直都在箱子里。
之因此关注装箱与拆箱,主要缘由就是他们的性能问题,并且在平常编码中,常常有装箱与拆箱的操做,并且这些装箱与拆箱的操做每每是在不经意时发生。通常来讲,装箱的性能开销更大,这不难理解,由于引用对象的分配更加复杂,成本也更高,值类型分配在栈上,分配和释放的效率都很高。装箱过程是须要建立一个新的引用类型对象实例,拆箱过程须要建立一个值类型字段,开销更低。
为了尽可能避免这种性能损失,尽可能使用泛型,在代码编写中也尽可能避免隐式装箱。
就是不经意的代码致使屡次重复的装箱操做,看看代码就好理解了
int x = 100; ArrayList arr = new ArrayList(3); arr.Add(x); arr.Add(x); arr.Add(x);
这段代码共有多少次装箱呢?看看Add方法的定义:
再看看IL代码,能够准确的获得装箱的次数:
显示装箱能够避免隐式装箱,下面修改后的代码就只有一次装箱了。
int x = 100; ArrayList arr = new ArrayList(3); object o = x; arr.Add(o); arr.Add(o); arr.Add(o);
装箱就是值类型转换为引用类型,拆箱就是引用类型(被装箱的对象)转换为值类型。
就是引用类型对象。
托管堆上。
装箱和拆箱都涉及到内存的分配和对象的建立,有较大的性能影响。
编码中,多使用泛型、显示装箱。
上面说了,箱子就是一个引用类型对象,所以她的结构,主要包含两部分:
int i = 5; 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