概念简述:
在装箱拆箱之前简单说一下值类型、引用类型:
值类型:原类型(Sbyte、Byte、Short、Ushort、Int、Uint、Long、Ulong、Char、Float、Double、Bool、Decimal)、枚举(enum)、结构(struct)等,是在栈中分配内存,在申明的同时就初始化,以确保数据不为NULL;
引用类型:类、数组、接口、委托、字符串等,在堆中分配内存,初始化为null,引用型是需要GARBAGE COLLECTION来回收内存的,值型不用,超出了作用范围,系统就会自动释放;
装箱:将值类型转换为引用类型,用于在垃圾回收堆中存储值类型,是值类型到 object 类型或到此值类型所实现的任何接口类型的隐式转换;
拆箱:将引用类型转换为值类型,从 object 类型到值类型或从接口类型到实现该接口的值类型的显式转换;
利用装箱和拆箱功能,可通过允许值类型的任何值与Object类型的值相互转换,将值类型与引用类型链接起来
简例:
int val = 100;
object obj = val;
Console.WriteLine (“对象的值 = {0}", obj);
这是一个装箱的过程,是将值类型转换为引用类型的过程
int val = 100;
object obj = val;
int num = (int) obj;
Console.WriteLine ("num: {0}", num);
这是一个拆箱的过程,是将值类型转换为引用类型,再由引用类型转换为值类型的过程
(注: .NET中,数据类型划分为值类型和引用(不等同于C++的指针)类型,与此对应,内存分配被分成了两种方式,一为栈,二为堆(托管堆)。值类型只会在栈中分配,引用类型分配内存与托管堆,托管堆对应于垃圾回收。)
何时装箱
1、 调用一个含类型为Object的参数的方法,该Object可支持任意为型,当你需要将一个值类型(如Int32)传入时,则需要装箱;
2、 一个非泛型的容器,同样是为了保证通用,而将元素类型定义为Object。若要将值类型数据加入容器时,则需要装箱;
装箱拆箱效率
装箱:
1、 首先从托管堆中为新生成的引用对象分配内存;
2、 然后将值类型的数据拷贝到刚刚分配的内存中;
3、 返回托管堆中新分配对象的地址;
进行一次装箱要进行分配内存和拷贝数据这两项比较影响性能的操作。
拆箱:
1、 首先获取托管堆中属于值类型那部分字段的地址,这一步是严格意义上的拆箱;
2、 将引用对象中的值拷贝到位于线程堆栈上的值类型实例中;
严格意义上的拆箱,并不影响性能,但伴随这之后的拷贝数据的操作就会同boxing操作中一样影响性能。
理论上,装箱时,生成的是全新的引用对象,这会有时间损耗,也就是造成效率降低。通过重载函数,也可以通过泛型来避免装箱。
对于装箱拆箱代码的优化,由于C#中对装箱和拆箱都是隐式的,所以,根本的方法是对代码进行分析,而分析最直接的方式是了解原理结何查看反编译的IL代码。比如:在循环体中可能存在多余的装箱,你可以简单采用提前装箱方式进行优化。
参考:
http://www.2cto.com/kf/201504/391285.html