值類型是數據的容器,它們不具備多太性。另一方面就是說,.Net框架被設 計成單一繼承的引用類型,System.Object,在整個繼承關系中做為根對象存在 。設計這兩種類型的目的是截然不同的,.Net框架使用了裝箱與拆箱來鏈接兩種 不同類型的數據。裝箱是把一個值類型數據放置在一個無類型的引用對象上,從 而使一個值類型在須要時可以當成引用類型來使用。拆箱則是額外的從“ 箱”上拷貝一份值類型數據。裝箱和拆箱可以讓你在須要使用 System.Object對象的地方使用值類型數據。但裝箱與拆箱操作卻是性能的強盜 ,在些時候裝箱與拆箱會產生一些臨時對象,它會導致程序存在一些隱藏的BUG 。應該盡可能的避免使用裝箱與拆箱。
裝箱可以把一個值類型數據轉化 也一個引用類型,一個新的引用對象在堆上創建,它就是這個“箱子 ”,值類型的數據就在這個引用類型中存儲了一份拷貝。參見圖2.3,演示 了裝箱的對象是如何訪問和存儲的。箱子中包含一份這個值類型對象的拷貝,並 且復制實現了已經裝箱對象的接口。當你想從這個箱子中取回任何內容時,一個 值類型數據的拷貝會被創建並返回。這就是裝箱與拆箱的關鍵性概念:對象的一 個拷貝存放到箱子中,而不管何時你再訪問這個箱子時,另一個拷貝又會被創建 。
圖2.3,值類型數據在箱子中。把一個值類型數據轉化成一個 System.Object的引用,一個無名的引用類型會被創建。值類型的數據就存儲在 這個無名的引用對象中,所有的訪問方法都要通過這個箱子才能到達值類型數據 存儲的地方。
最陰險的地方是這個裝箱與拆箱很多時候是自動完成的!當 你在任何一個期望類型是System.Object的地方使用值類型數據時,編譯器會生 成裝箱與拆箱的語句。另外,當你通過一個接口指針來訪問值類型數據時,裝箱 與拆箱也會發生。當你裝箱時不會得到任何警告,即使是最簡單的語句也一樣。 例如下面這個:
Console.WriteLine("A few numbers:{0}, {1}, {2}",
25, 32, 50);
使用重載的 Console.WriteLine函數須要一個System.Object類型的數組引用,整型是值類型 ,所以必須裝箱後才能傳給重載的WriteLine方法。唯一可以強制這三個整數成 為System.Object對象的方法就是把它們裝箱。另外,在WriteLine內部,通過調 用箱子對象上的ToString()方法來到達箱子內部。某種意義上講,你生成了這樣 的結構:
int i =25;
object o = i; // box
Console.WriteLine(o.ToString());
在WriteLine內部,下面 的執行了下面的代碼:
object o;
int i = ( int )o; // unbox
string output = i.ToString( );