學習一項新知識的時候,最好的方法就是去實踐它。
《clr via c#》這本神書真的是太有意思了!沒錯我的前言就是這個。
首先來看下,下面這段**
可以看到,每次迴圈迭代都會初始化乙個point的值型別字段,並將該point儲存到arraylist中。
但是我們肯定有疑問,arraylist中究竟儲存了什麼?是point結構,point結構的位址,還是其他完全不同的東西?
為了解答這個問題,我們必須研究一下arraylist的add方法,了解它的引數被定義成了什麼型別。
可以知道,add的原型如下:
1說明add獲取的是乙個object引數。也就是說,add獲取對託管對上的乙個物件的引用(或指標)來作為引數。但是之前的**傳遞的是p,也就是乙個point,是乙個值型別。public
virtual int32 add(object value);
為了使**能夠正確的工作,point值型別必須轉換成真正的、在堆中託管的物件,而且必須獲取對該物件的引用。
那麼,這個將值型別轉換成引用型別的操作,就叫做裝箱。
當值型別的實力進行裝箱時,發生了以下三件事情:
1、在託管堆中分配記憶體。分配的記憶體量是值型別各個欄位所需要的記憶體量,還要加上託管堆所有物件都有的兩個額外成員(型別物件指標和同步塊索引)所需要的記憶體量。關於型別物件指標和同步塊索引,可以看我的這邊博文:
2、值型別的字段複製到新分配的堆記憶體。
3、返回物件位址。現在該位址是物件引用;值型別成了引用型別。
c#編譯器自動生成堆值型別實力進行裝箱所需的il**。
所以在執行時,當前存在於point值型別實力p中的字段複製到新分配的point物件中。已經裝箱的point物件(現在是引用型別)的位址返回並傳給add方法。point物件一直存在於堆中,直至被垃圾**。
(所以之所以說相比於arraylist,盡量用泛型list,因為泛型集合list允許開發人員在操作值型別的集合時不需要對集合中的項進行裝箱或拆箱操作。這一改進,使效能提高了不少,因為託管堆中需要建立的物件減少了,進而減少了應用程式需要執行的垃圾**的次數。)
說完了裝箱,現在來說說拆箱。假定我繼續執行以下操作:
它獲取arraylist的元素0包含的引用(或指標),試圖將其放到point值型別的實力p2中。
clr分為兩步完成複製:
1、獲取已經裝箱point物件中的各個point欄位的位址。這個過程就是拆箱。
2、將字段包含的值從堆複製到基於棧的值型別的例項中。
拆箱並不是直接將裝箱過程倒過來,拆箱的代價要低的多。拆箱其實就是獲取指標的過程,該指標指向包含在乙個物件中的原始值型別(資料字段)。
其實指標指向的是已經裝箱例項中的未裝箱部分。所以和裝箱不同,拆箱不要求在記憶體中複製任何位元組。
已經裝箱的值型別例項在進行拆箱時,內部發生了下面這些事情:
1、如果包含「對已經裝箱值型別例項的引用」的變數為null,則丟擲nullreferenceexception異常。
2、如果引用的物件不是所需值型別的已裝箱例項,丟擲invalidcastexception異常。
C 裝箱 拆箱 理解
裝箱和拆箱是值型別和引用型別之間相互轉換是要執行的操作。1.裝箱在值型別向引用型別轉換時發生 2.拆箱在引用型別向值型別轉換時發生 光上述兩句話不難理解,但是往深處了解,就需要一些篇幅來解釋了。我們先看裝箱時都會發生什麼事情,下面是一行最簡單的裝箱 objectobj 1 這行語句將整型常量1賦給o...
C 拆箱與裝箱 (筆記)
裝箱就是 自動將基本資料型別轉換為包裝器型別 拆箱就是 自動將包裝器型別轉換為基本資料型別。拆箱與裝箱就是值型別與引用型別的轉換,她是值型別和引用型別之間的橋梁,他們可以相互轉換的乙個基本前提就是上面所說的 object是.net中的萬物之源 先看看乙個小小的例項 int x 1023 object...
關於裝箱和拆箱的學習筆記
最早的時候,我以為將物件轉換為object型別就是裝箱,將轉換後的ojbect轉換回具體的型別就是拆箱。後來才知道,值型別和引用型別之間的互相轉換才叫裝箱,拆箱。裝箱和拆箱是有一定的效能損耗的,盡量避免無意義的裝箱拆箱,比如用泛型。int x 2 object o x 這裡是裝箱 生成乙個新的引用物...