天天看點

18.1.4 連鎖推導法:為什麼值類型不可以為null而引用類型可以為null呢?

連鎖推導法:在一個證明過程中,或一個比較複雜的推理過程中,将前一個推理的結論作為後一個推理的前提,一步接一步地推導,直到把需要的結論推出來。

我們在前面的知識中了解到值類型存儲在堆棧(Stack)中,而引用類型存儲在托管堆(Heap)中,堆棧的工作方式是以先進後出原則先保證先配置設定記憶體的變量後釋放,你可以想像的出,堆棧中的變量是從後向前釋放,這樣就保證了堆棧中先進後出的規則不與變量的生命周期起沖突。

你可以仔細的想一下關于結構化程式設計的一些規則,C#對變量的聲明要求是先定義後使用,變量的生命周期是從其定義開始直到程式的控制離開該變量所在的{}。以下代碼描述了這個我們非常熟悉的事實

static void Main(string[] args)

{

int k = 10;//k的生命周期開始了

for (int i = 0; i <= 10 - 1; i++)//i的生命周期開始了

int m = k + i;//m的生命周期開始了

for (int j = i; j <= 10 - 1; j++)//j的生命周期開始了

int n = j * i;//n的生命周期開始了

//n的生命周期結束了

}//j的生命周期結束了

//m的生命周期結束了

}//i的生命周期結束了

//k的生命周期結束了

}

下面的圖描述了這些變量的生命周期和堆棧的存儲

<a href="http://blog.51cto.com/attachment/201203/195759131.gif" target="_blank"></a>

同時我們在C#的程式設計中可以發現一個非常有趣的現象,即我們不能将值類型設定為null,但可以對引用類型設定為null。

假設上述的代碼改為如下形式

k = null; //k的生命周期結束了???

你考慮下第七行代碼

這時候棧應該怎麼處理呢?想像下,堆棧圖會怎麼樣呢?顯然堆棧到了第三步就不知道怎麼樣才可以把變量k銷毀了。

<a href="http://blog.51cto.com/attachment/201203/195851736.gif" target="_blank"></a>

那為什麼引用類型就可以設定為null呢?我們先看如下的代碼

System.Collections.ArrayList k = new System.Collections.ArrayList();//k的生命周期開始了

k.Add(i);

int m = k.Count;//m的生命周期開始了

對應的堆棧中的處理大緻如圖

<a href="http://blog.51cto.com/attachment/201203/195907485.gif" target="_blank"></a>

我們可以看出變量k還是配置設定在堆棧中,但實際存放ArrayList執行個體的區域卻是存儲在堆中。對ArrayList的執行個體使用,是通過在堆棧中的變量k來間接的指向的。這樣的好處是,将對象引用null和生命周期兩個概念可以分離出來。

以下代碼請仔細認知

<a href="http://blog.51cto.com/attachment/201203/195928105.gif" target="_blank"></a>

我們可以很清晰的觀察到,k的生命周期并沒有發生變化,隻是當k = null;後,變量k不再指向堆中的有效位址了。

現在我們明白了,因為值類型變量直接在Stack中儲存了資料,是以在生命周期結束前資料不能被任何形式的銷毀,而引用類型變量在Heap中儲存資料,是以指派null其實是将對應在Heap中的資料銷毀而不是結束變量的生命周期。

本文轉自shyleoking 51CTO部落格,原文連結:http://blog.51cto.com/shyleoking/803155

繼續閱讀