首页 > 代码库 > 装箱和拆箱 深度理解

装箱和拆箱 深度理解

提问:

首先我们来提两个疑问,我们自定义了一个类如Customclass类型,

Customclass myclass=new Customclass()

Object obj=myclass;

运行上面这段代码,我们会进行装箱操作吗?

 

基础知识:

.Net的类型分为两种,一种是值类型(Value Type ),另一种是引用类型(Reference Type)。这两个类型的本质区别,值类型数据是分配在栈中,而引用类型数据分配在堆上。那么如果要把一个值类型数据放到堆上,就需要装箱操作;反之,把一个放在堆上的值类型数据取出来,则需要进行拆箱操作。

说到具体程序代码上, 我们可以这样总结: 装箱是将值类型转换为引用类型 ;拆箱是在已装箱的前提下将引用类型转换为值类型.

我们要充分的了解装箱和拆箱,首先我们先得了解一下.net framework平台的类的构造.在这个平台上有一个万物之源,那就是System.Object类型,在之下系统又分了值类型(Value Type )和引用类型(Reference Type).值类型包括原类型(Sbyte、Byte、Short、Ushort、Int、Uint、Long、Ulong、Char、Float、Double、Bool、Decimal)、枚举(enum)、结构(struct),引用类型包括:类、数组、接口、委托、字符串等。所以到这里我们就可以得出一个结论,文章开头提的那个问题的答案也就不言而喻了.我们自定义声明的类型是一种引用类型,所以我们把他放到object里面的时候,是不会发生装箱操作的.

 

具体操作:

下面我们来解析一下装箱和拆箱系统做了那些操作

前面我们已经说到.值类型数据是分配在栈(stack)中,他是一个先进后出的结构,由编译器自动分配释放 ,存放函数的参数值,局部变量的值等.而引用类型数据分配在堆(heap)上,其操作方式类似于数据结构中的栈。堆他是一种顺序随意的结构,一般由程序员分配释放若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。具体在.net框架上的情况你可以阅读  .NET中栈和堆的比较 这篇文章,这位博主翻译了四篇关于这方面的文章,为中国的IT视野做出了巨大的贡献啊.

我们来结合一个小例子说明一下装箱操作:

int i = 123; object o = (object)i;

对值类型在堆中分配一个对象实例,并将该值复制到新的对象中。按三步进行。 
第一步:新分配堆内存(大小为值类型实例大小加上一个方法表指针和一个SyncBlockIndex)。 
第二步:将值类型的实例字段拷贝到新分配的内存中。 
第三步:返回堆中新分配对象的地址。这个地址就是一个指向对象的引用了。

技术分享

我们通过这种方式将一个值类型的int i装箱为一个object类型的 o变量

拆箱就是一个逆过程了.将一个object 类型的o变量还原为int型的i变量,它进行了如下操作:

(1)环境须先判断堆栈上指向合法对象的地址,以及在对此对象向指定的类型进行转换时是否合法,如果不合法,就抛出异常;

(2)当判断类型转换正确,就返回一个指向对象内的值的指针。

针对上面的合法性判断,有两点需要我们注意:

(1)包含已装箱的值类型的引用的变量如果为null,就抛出一个NullReferenceException异常。

(2)如果引用指向的对象不是所要求的值类型的一个已装箱的实例,就抛出一个InvaildCastException异常

 

 

带来的影响:

我们可以从两个方面考虑:一个就是对于堆的操作效率比较低;另一个就是对于堆上分配的内存资源,需要GC来回收,从而降低程序效率。

显然,直观的来说.装箱时,生成的是全新的引用对象,这会有时间损耗,也就是造成效率降低。 
当然我们也可以直观的看出,拆箱所带来的性能损失是远小于装箱所带来的损失的.

 

 

OK.已经很晚了,明天再来发一篇文章主要介绍我们在编程中怎么解决装箱拆箱带来的性能损失.各位晚安.

 

装箱和拆箱 深度理解