1. 装箱就是将值类型(value type)转换为引用类型(reference type)的过程.
利用装箱和拆箱的功能,允许值类型与object类型的值相互转换.
从栈上把值封装到托管堆上就是装箱,而从托管堆吧值封送的栈上就拆箱
int i=0;
Syste.Object obj=i;//将i装箱!
int j=(int)obj;//将obj拆箱!
注:被装过箱的对象才能被拆箱.
2. .NET中,数据类型划分为值类型和引用类型(不等同于C++中的指针),于此对应,内存分配被分成了两种方式,一为栈,二为堆(注意是托管堆)
值类型只会在栈中分配内存,在声明的时候同时初始化,确保数据不为null.超出作用范围,系统会自动释放内存.
引用类型分配内存与托管堆.需要GC来回收内存.
3. 为什么需要装箱?
一种最普通的场景是,调用一个含类型为Object的参数的方法,该Object可支持任意为型,以便通用。当你需要将一个值类型(如Int32)传入时,需要装箱。
另一种用法是,一个非泛型的容器,同样是为了保证通用,而将元素类型定义为Object。于是,要将值类型数据加入容器时,需要装箱。
4. 装箱/拆箱的内部操作
装箱:对值类型在堆中分配一个对象实例,并将该值复制到新的对象中.
1. 划分堆内存(大小=值类型实例的大小+对象及其结构所占用的空间)
对象结构所占的空间=一个方法表指针+一个SyncBlockIndex
2. 将值类型的实例字段拷贝到新分配的内存中.
3. 返回托管堆中新分配对象的地址,这个地址就是一个指向对象的引用.。值类型实例也就变成了一个引用类型对象。
利用装箱和拆箱的功能,允许值类型与object类型的值相互转换.
从栈上把值封装到托管堆上就是装箱,而从托管堆吧值封送的栈上就拆箱
int i=0;
Syste.Object obj=i;//将i装箱!
int j=(int)obj;//将obj拆箱!
注:被装过箱的对象才能被拆箱.
2. .NET中,数据类型划分为值类型和引用类型(不等同于C++中的指针),于此对应,内存分配被分成了两种方式,一为栈,二为堆(注意是托管堆)
值类型只会在栈中分配内存,在声明的时候同时初始化,确保数据不为null.超出作用范围,系统会自动释放内存.
引用类型分配内存与托管堆.需要GC来回收内存.
3. 为什么需要装箱?
一种最普通的场景是,调用一个含类型为Object的参数的方法,该Object可支持任意为型,以便通用。当你需要将一个值类型(如Int32)传入时,需要装箱。
另一种用法是,一个非泛型的容器,同样是为了保证通用,而将元素类型定义为Object。于是,要将值类型数据加入容器时,需要装箱。
4. 装箱/拆箱的内部操作
装箱:对值类型在堆中分配一个对象实例,并将该值复制到新的对象中.
1. 划分堆内存(大小=值类型实例的大小+对象及其结构所占用的空间)
对象结构所占的空间=一个方法表指针+一个SyncBlockIndex
2. 将值类型的实例字段拷贝到新分配的内存中.
3. 返回托管堆中新分配对象的地址,这个地址就是一个指向对象的引用.。值类型实例也就变成了一个引用类型对象。
拆箱:检查对象实例,去报它是给定值类型的装箱值,将该值从实例复制到值类型变量中.
1. 首先获取已装箱对象中属于值类型的那部分字段的地址。(这个过程其实才是拆箱unboxing)。
2. 然后将这些字段的值从托管堆拷贝到位于线程堆栈上的值类型的实例中。(字段拷贝)
5.装箱/拆箱对执行效率的影响
装箱时,生成的是全新的引用对象,会有时间损耗造成效率降低.可以通过重载函数和泛型类避免.重载函数:参数直接定义使用值类型,避免传入参数的装箱操作.
严格意义上的拆箱是不影响性能的.
6. 实例
int x = 3;
Console.WriteLine(object.ReferenceEquals(x, x));// false
因为x被两次装箱到两个不同的对象中去了。