读CLR via C#总结(4) 值类型的装箱和拆箱
值類型是比引用類型更"輕型"的一種類型,因?yàn)樗鼈儾蛔鳛閷?duì)象在托管堆中分配,不會(huì)被垃圾回收,也不通過指針來引用。但在許多情況下,都需要獲取對(duì)值類型的一個(gè)實(shí)例引用。為了將一個(gè)值類型轉(zhuǎn)換成一個(gè)引用類型,要使用一個(gè)名為裝箱(boxing)的機(jī)制。
一,裝箱
裝箱即將一個(gè)值類型轉(zhuǎn)換成一個(gè)引用類型,下面是總結(jié)對(duì)值類型的一個(gè)實(shí)例進(jìn)行裝箱時(shí)內(nèi)部發(fā)生的事情:
1,在托管堆中分配內(nèi)存。內(nèi)存量是值類型的各個(gè)字段需要的內(nèi)存量加上托管堆的所有對(duì)象都有的兩個(gè)額外成員(類型對(duì)象指針和同步塊索引)需要的內(nèi)存量。
2,將值類型的字段復(fù)制到新分配的堆內(nèi)存。
3,返回對(duì)象的地址(或稱指針)。現(xiàn)在,這個(gè)地址是對(duì)一個(gè)對(duì)象的引用,值類型現(xiàn)在是一個(gè)引用類型。
例如:代碼如下:
namespace ValueTypeDemo2{
//聲明一個(gè)值類型
struct Point
{
public int x, y;
}
class Program
{
static void Main(string[] args)
{
ArrayList a = new ArrayList();
Point p;//分配一個(gè)Point(不在堆中分配)
for (int i = 0; i < 10; i++)
{
p.x = p.y = i;//初始化值類型中的成員
a.Add(p);//對(duì)值類型進(jìn)行裝箱,并將引用添加到ArrayList中,因?yàn)锳dd()方法需要的是一個(gè)Object對(duì)象
}
}
}
}
二,拆箱
拆箱實(shí)際上就是獲取一個(gè)指向包含在一個(gè)對(duì)象中的原始值類型(數(shù)據(jù)字段)的指針,往往會(huì)緊接著拆箱操作后發(fā)生一次字段復(fù)制的操作,將堆中的字段復(fù)制到基于棧的值類型的實(shí)例中。
例如:代碼如下:
CLR實(shí)際上分兩步完成這個(gè)復(fù)制操作:
1,獲取已裝箱的Point對(duì)象中的各個(gè)Point字段的址,這個(gè)過程稱為拆箱(unboxing)。
2,將這些字段包含的值從堆中復(fù)制到基于棧的值類型的實(shí)例中。注意,這個(gè)過程不屬于拆箱的范圍。
下面是一個(gè)已裝箱值類型實(shí)例在拆箱時(shí)內(nèi)部發(fā)生的事情:
1,如果包含"對(duì)已裝箱值類型實(shí)例的引用"的變量為Null,就拋出一個(gè)NullReferenceException異常。
2,如果引用指向的對(duì)象不是期待的值類型的一個(gè)已裝箱實(shí)例,就拋出一個(gè)InvalidCastException異常。
3,返回一個(gè)指向包含在一個(gè)對(duì)象中的原始值類型的指針。
下面是一個(gè)綜合實(shí)例,代碼如下:
namespace ValueTypeDemo3{
//聲明一個(gè)值類型
struct Point
{
public int x, y;
}
class Program
{
static void Main(string[] args)
{
Point p;//創(chuàng)建Point的實(shí)例,在棧上分配
p.x = p.y = 1;//初始化值類型的成員
object o = p;//對(duì)p進(jìn)行裝箱,o引用已裝箱的實(shí)例
p = (Point)o;//對(duì)o進(jìn)行拆箱,將字段從已裝箱的實(shí)例中復(fù)制到棧變量中
}
}
}
生成的IL代碼如下圖,可以看到程序發(fā)生了一次裝箱和一次拆箱:
與50位技術(shù)專家面對(duì)面20年技術(shù)見證,附贈(zèng)技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的读CLR via C#总结(4) 值类型的装箱和拆箱的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: align=absmiddle 是什么意
- 下一篇: C#基础总结