C#--装箱和拆箱

我们都知道C#有装箱和拆箱的操作,装箱是把基本类型用它们相应的引用类型包装起来,使其具有对象的性质;而拆箱则是将引用类型的对象简化成值类型的数据。如:

int i=0;
object o = (object)i; //装箱
i=(int)o;  //拆箱

转换:

  装箱 将值类型转换成引用类型

  拆箱 将引用类型转换成值类型

C#中,数据类型划分为值类型和引用类型,与此对应,内存分配被分成了两种方式,一为栈,二为堆,注意:是托管堆。值类型只会在栈中分配,引用类型分配内存与托管堆。(托管堆对应于垃圾回收)

PS:o 和 i 的改变将互不影响,因为装箱使用的是 i 的一个副本。

对值类型在堆中分配一个对象实例,并将该值复制到新的对象中。
1:首先从托管堆中为新生成的引用对象分配内存。 
2:然后将值类型的数据拷贝到刚刚分配的内存中。 
3:返回托管堆中新分配对象的地址,这个地址就是一个指向对象的引用了。
可以看出,进行一次装箱要进行分配内存和拷贝数据这两项比较影响性能的操作。

PS:o 和 i 的改变将互不影响。

1、首先获取托管堆中属于值类型那部分字段的地址,这一步是严格意义上的拆箱。
2、将引用对象中的值拷贝到位于线程堆栈上的值类型实例中。
经过这2步,可以认为是同boxing是互反操作。严格意义上的拆箱,并不影响性能,但伴随这之后的拷贝数据的操作就会同boxing操作中一样影响性能。

对执行效率的影响:

显然,从原理上可以看出,装箱时,生成的是全新的引用对象,这会有时间损耗,也就是造成效率降低。 那该如何做呢? 
首先,应该尽量避免装箱。 比如上例的两种情况,都可以避免,在第一种情况下,可以通过重载函数来避免。第二种情况,则可以通过泛型来避免。 当然,凡事并不能绝对,假设你想改造的代码为第三方程序集,你无法更改,那你只能是装箱了。 对于装箱/拆箱代码的优化,由于C#中对装箱和拆箱都是隐式的,所以,根本的方法是对代码进行分析,而分析最直接的方式是了解原理结合查看反编译的IL代码。比如:在循环体中可能存在多余的装箱,你可以简单采用提前装箱方式进行优化。

使用场景:

一般来说,装箱操作主要用于这两种场合:

1.赋值时使用:

在对Integer类型的变量直接赋值时会发生自动装箱操作,将Integer对象赋值给int时会发生自动拆箱操作。当赋值大于127时,Integer每赋值一次都会产生一个新的Integer对象。

2.调用方法,传递对象参数类型时:

比如我们不能直接在集合中放入基本类型值,因为集合只接收对象类型。我们可以将基本类型的值转换成对象,然后将这些转换的对象放入集合中。

再说一句:

目前在C#中,肯定不会再继续用ArrayList来存储一些对象的集合了,因为有了一组新的泛型集合,例如用List<T>:

List<int> listInt=new List<int>();//声明一个int类型的泛型集合
listInt.Add(1);//添加一条数据

在使用的时候就规定是什么类型,在存取数据的时候,不需要再进行多余的装箱和拆箱操作。但是在写代码的时候还是会隐藏很多拆箱和装箱的过程,注意尽量避免装箱和拆箱的操作,如果不可避免,那就尽量减少装箱和拆箱的操作,可以查看下方简单示例(无实际意义):

ArrList arrayList =new ArrayList();
int number =1;//定义值类型

arrayList.Add(number);//三次添加会造成三次装箱
arrayList.Add(number);
arrayList.Add(number);

object o =number;//提前进行一次装箱操作
arrayList.Add(o);//三次添加并不会造成装箱操作
arrayList.Add(o);
arrayList.Add(o);

 

评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

宋文轩

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值