在说装箱和拆箱之前,我们先说说什么是值类型和引用类型。在说值类型和引用类型之前,我们先说说什么是内存中的堆区和栈区。
栈内存:
由程序自动向操作系统申请分配以及回收,速度快,使用方便,但程序员无法控制。若分配失败,则提示栈溢出错误。
注意,const局部变量也储存在栈区内。
int i = 10; //变量i储存在栈区中
堆内存:
程序员向操作系统申请一块内存,当系统收到程序的申请时,会遍历一个记录空闲内存地址的链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。分配的速度较慢,地址不连续,容易碎片化。此外,由程序员申请,同时也必须由程序员负责销毁,否则导致内存泄露。
int* pi = new int(100); //指针pi指向的内存是在堆区,专门储存程序运行时分配的内存
在说装箱和拆箱之前,我们先说说什么是值类型和引用类型。
值类型:(int long float double char bool enum struct)
1.值类型变量都存储在内存的栈中。
2.值类型的变量直接存放实际的数据,而不是变量的地址。
3.值类型变量不能为null,必须具有一个确定的值。
4.值类型继承自System.ValueType。
5.值类型存取速度快。
int i=1;//创建一个值类型i
int j=i;//创建一个新的值类型j,之后再把i的值拷贝给j
值类型通常被人们称为轻量级的类型,因为在大多数情况下,值类型的的实例都分配在线程栈中,因此它不受垃圾回收的控制,缓解了托管堆中的压力,减少了应用程序的垃圾回收的次数,提高性能。
引用类型:(string class int[] string[] object )
1.引用类型的数据存储在内存的堆中。
2.引用类型存取速度慢。
3.引用类型表示指向存储在内存堆中的数据的指针或引用。
4.引用类型继承自System.Object
5.引用类型的变量存放的则是数据的地址,即对象的引用。
6.引用类型的变量把实际数据的地址保存在栈中,而实际数据则保存在堆中。
有关性能问题:
- 在设计一个应用程序时,如果都是引用类型,那么应用程序的性能将显著下降,因为这会加大托管堆的压力,增加垃圾回收的次数。
- 由于值类型实例的值是自己本身,而引用类型的实例的值是一个引用,所以如果将一个值类型的变量赋值给另一个值类型的变量,会执行一次逐字段的复制,将引用类型的变量赋值给另一个引用类型的变量时,只需要复制内存地址,所以在对大对象进行赋值时要避免使用值类型
装箱:
封箱是把值类型转换为引用类型
int i = 3 ; //分配在栈上
object o = i ;//隐式装箱操作,int i 在堆上
object b = (object)i ; //显示装箱操作
装箱的内部操作:
第一步:新分配托管堆内存(大小为值类型实例大小加上一个方法表指针和一个SyncBlockIndex(同步块索引))。
第二步:将值类型的实例字段拷贝到新分配的内存中。
第三步:返回托管堆中新分配对象的地址。这个地址就是一个指向对象的引用了。
拆箱:
拆箱是把引用类型转换为值类型
int j = (int) o ;//显示拆箱(将对象o拆箱为int类型)
拆箱的内部操作:
1,检查对象实例,以却确保它是给定值类型的装箱值。
2,将该值从实例复制到值类型变量中。
注意:不能隐式拆箱!!!
装箱和拆箱的优点与缺点:
优点:能够在值类型和引用类型中架起一做桥梁,可以轻松的实现值类型与引用类型的互相转换。
缺点:装箱时生成的是全新的引用对象,会有时间空间的损耗,造成效率降低。应该尽量避免使用装拆箱。或者使用泛型和重载来避免。
为什么需要装箱?
一种最普通的场景是,调用一个含类型为Object的参数的方法,该Object可支持任意为型,以便通用。当你需要将一个值类型(如Int32)传入时,需要装箱。
另一种用法是,一个非泛型的容器,同样是为了保证通用,而将元素类型定义为Object。于是,要将值类型数据加入容器时,需要装箱。