标签:
谈到装箱拆箱,DebugLZQ相信给位园子里的博友一定可以娓娓道来,大概的意思就是值类型和引用类型的相互转换呗---值类型到引用类型叫装箱,反之则叫拆箱。这当然没有问题,可是你只知道这么多,那么DebugLZQ建议你花点时间看看楼主这篇文章,继续前几篇博文的风格--浅谈杂侃。
为了说明装箱和拆箱,那首先必须先说类型。在.NET中,我们知道System.Object类型是所有内建类型的基类。注意这里说的是内建类型,程序员可以编写不继承子自System.Object的类型,这里不做过多的介绍(感兴趣的博友可以研究一下)。
所有.NET的类型都可以分为两类(有点不严谨,但是大家都这么讲):值类型和引用类型。那么值类型和引用类型如何区分,标准是什么?最简单也最明确的一个区分标准是:所有的值类型都继承自System.ValueType(System.ValueType继承自System.Object),也就是说,所有继承自System.ValueType的类型都是值类型,而其他类型都是引用类型。(题外话:以前在读一位博友王涛的《你必须知道的.NET》中,他说,值类型和引用类型最本质的区别是:值类型和引用类型在内存中分配的位置不同,前者分配在堆栈上,后者分配在堆上。个人觉得这个不是一个简单明确的区分方法。远没有DebugLZQ说的这么露骨!)
说到这里,你应该要有这样的想法:严格来说的话,System.Object作为所有内建类型的基类,本身并没有值类型和引用类型之分。但是System.Object的对象,具有引用类型的特点。这也是值类型在有些场合需要装箱拆箱的原因。
下面还是简单说下值类型和引用类型的不一样的地方吧,分3块,个人觉得理解这3块就可以了:
前面简单介绍了.NET中的类型,下面引入装箱和拆箱。通过1我们知道值类型的对象是在堆栈上分配内存的,而引用类型(包括System.Object)对象是在堆上分配内存的,那么当值类型被类型转换时,会在堆栈和堆上进行一系列的操作,这就是装箱拆箱的来源。
充分理解装箱拆箱的原理,有助于我们程序员写出高效的代码。
梳理下:前面DebugLZQ说到,所有值类型都继承自System.ValueType,而Sytem.ValueType继承自System.Object;所有值类型对象都分配在堆栈上,而所有引用类型,当然包括System.Object,对象都分配在堆上。那么,问题来了:既然System.Object 是所有值类型的基类,那么所有值类型必然可以隐式转换成System.Object(面向对象中的类型替换原则,基类能够替换子类),那么这个对象将被分配在哪里,堆上还是堆栈上?事实上,当这个转换发生时,CLR需要做额外的工作把堆栈上的值类型移动到堆上,这个操作就是被我们称作的“装箱”。
这个步骤不需要程序员自己编写,在任何出现装箱的地方,编译器会自动加上执行以上功能的IL代码。
所谓的拆箱就是装箱对应着的概念,但拆箱的过程和装箱并不是倒过来就是:
如果为待拆箱对象为null,抛出NullReferenceException异常。
如果引用指向的不是一个期望对象的已装箱对象,抛出InvalidCastException异常。
需要说明的是一般拆箱以后会伴随着对象的拷贝,但拷贝操作已经不是拆箱的范畴。
了解了装箱和拆箱的操作,我们可以清楚的明白:装箱操作会导致数据在堆和栈上进行拷贝,频繁的装箱操作会性能损失。而相比而言拆箱过程对性能损耗还是比较小的。
装箱和拆箱意味着堆和堆栈空间的一系列操作,毫无疑问,这些操作的性能代价是很大的,尤其是对于堆上空间的操作,速度相对于堆栈慢得多,并且可能引发垃圾回收,这些都将大规模的影响系统系能。
装箱和拆箱操作经常发生在以下连个场合:
第一种情况,类型的格式化输出往往伴随一次装箱操作,譬如:
using System; namespace MaxValueTest { /// <summary> /// DebugLZQ /// http://www.cnblogs.com/DebugLZQ /// </summary> class Program { static void Main(string[] args) { int i = Int32.MaxValue; Console.WriteLine("Int32的最大值是"+i);//引发了一次不必要的装箱操作 Console.WriteLine("Int32的最大值是" + i.ToString());//ok Console.ReadKey(); } } }
第二种情况更为常见一些,例如常用的容器ArrayList,就是一个典型的System.Object容器,任何值类型被放入到ArrayList的对象中,都会发生一次装箱操作,而对应的取出值类型对象会引发一次拆箱操作。
在.NET 2.0以后,引入了“泛型”的概念后,这些问题得到了有效的解决。泛型允许定义针对某个特定类型(包括值类型)的容器,并且避免装箱和拆箱。
关于泛型的机制和原理,请关注DebugLZQ后面的博文:《浅谈.NET中的泛型的机制和原理》,请期待~
标签:
原文地址:http://www.cnblogs.com/feige/p/5592317.html