C#中的装箱与拆箱 值类型与引用类型 内存中的堆区与栈区

在说装箱和拆箱之前,我们先说说什么是值类型和引用类型。在说值类型和引用类型之前,我们先说说什么是内存中的堆区和栈区。

栈内存:

由程序自动向操作系统申请分配以及回收,速度快,使用方便,但程序员无法控制。若分配失败,则提示栈溢出错误。

注意,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。于是,要将值类型数据加入容器时,需要装箱。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值