裝箱和拆箱幾乎是所有面試題中必考之一,看上去簡單,就往往容易被忽視。其實它一點都不簡單的,一個簡單的問題也可以從多個層次來解讀。
1.什麼是拆箱和裝箱?
2.什麼是箱子?
3.箱子放在哪裡?
4.裝箱和拆箱有什麼性能影響?
5.如何避免隱身裝箱?
6.箱子的基本結構?
7.裝箱的過程?
8.拆箱的過程?
9.下面這段代碼輸出什麼?共發生多少次裝箱?多少次拆箱?
int i = 5; object obj = i; IFormattable ftt = i; Console.WriteLine(System.Object.ReferenceEquals(i, obj)); Console.WriteLine(System.Object.ReferenceEquals(i, ftt)); Console.WriteLine(System.Object.ReferenceEquals(ftt, obj)); Console.WriteLine(System.Object.ReferenceEquals(i, (int)obj)); Console.WriteLine(System.Object.ReferenceEquals(i, (int)ftt));
有拆必有裝,有裝必有拆。
在上一文中我們提到,所有值類型都是繼承自System.ValueType,而System.ValueType又是來自何方呢,不難發現System.ValueType繼承自System.Object。因此Object是.NET中的萬物之源,幾乎所有類型都來自她,這是裝箱與拆箱的基礎。
特別注意的是,本文與上一文有直接關聯,需要先了解上一文中值類型與引用類型的原理,才可以更好理解本文的內容。
拆箱與裝箱就是值類型與引用類型的轉換,她是值類型和引用類型之間的橋梁,他們可以相互轉換的一個基本前提就是上面所說的:Object是.NET中的萬物之源
先看看一個小小的實例代碼:
int x = 1023; object o = x; //裝箱 int y = (int) o; //拆箱
裝箱:值類型轉換為引用對象,一般是轉換為System.Object類型或值類型實現的接口引用類型;
拆箱:引用類型轉換為值類型,注意,這裡的引用類型只能是被裝箱的引用類型對象;
由於值類型和引用類型在內存分配的不同,從內存執行角度看,拆箱與裝箱就勢必存在內存的分配與數據的拷貝等操作,這也是裝箱與拆箱性能影響的根源。
int x = 1023; object o = x; //裝箱
裝箱就是把值類型轉換為引用類型,具體過程:
如上圖所示,裝箱後內存有兩個對象:一個是值類型變量x,另一個就是新引用對象o。裝箱對應的IL指令為box
,上面裝箱的IL代碼如下圖:
int x = 1023; object o = x; //裝箱 int y = (int) o; //拆箱
明白了裝箱,拆箱就是裝箱相反的過程,簡單的說是把裝箱後的引用類型轉換為值類型。具體過程:
如上圖所示,拆箱後,得到一個新的值類型變量y,拆箱對應的IL指令為unbox
,拆箱的IL代碼如下:
通過上面深入了解了裝箱與拆箱的原理,不難理解,只有值類型可以裝箱,拆的就是裝箱後的引用對象,箱子就是一個存放了值類型字段的引用對象實例,箱子存儲在托管堆上。只有值類型才有裝箱、拆箱兩個狀態,而引用類型一直都在箱子裡。
之所以關注裝箱與拆箱,主要原因就是他們的性能問題,而且在日常編碼中,經常有裝箱與拆箱的操作,而且這些裝箱與拆箱的操作往往是在不經意時發生。一般來說,裝箱的性能開銷更大,這不難理解,因為引用對象的分配更加復雜,成本也更高,值類型分配在棧上,分配和釋放的效率都很高。裝箱過程是需要創建一個新的引用類型對象實例,拆箱過程需要創建一個值類型字段,開銷更低。
為了盡量避免這種性能損失,盡量使用泛型,在代碼編寫中也盡量避免隱式裝箱。
就是不經意的代碼導致多次重復的裝箱操作,看看代碼就好理解了
int x = 100; ArrayList arr = new ArrayList(3); arr.Add(x); arr.Add(x); arr.Add(x);
這段代碼共有多少次裝箱呢?看看Add方法的定義:
再看看IL代碼,可以准確的得到裝箱的次數:
顯示裝箱可以避免隱式裝箱,下面修改後的代碼就只有一次裝箱了。
int x = 100; ArrayList arr = new ArrayList(3); object o = x; arr.Add(o); arr.Add(o); arr.Add(o);
裝箱就是值類型轉換為引用類型,拆箱就是引用類型(被裝箱的對象)轉換為值類型。
就是引用類型對象。
托管堆上。
裝箱和拆箱都涉及到內存的分配和對象的創建,有較大的性能影響。
編碼中,多使用泛型、顯示裝箱。
上面說了,箱子就是一個引用類型對象,因此她的結構,主要包含兩部分:
int i = 5; object obj = i; IFormattable ftt = i; Console.WriteLine(System.Object.ReferenceEquals(i, obj)); Console.WriteLine(System.Object.ReferenceEquals(i, ftt)); Console.WriteLine(System.Object.ReferenceEquals(ftt, obj)); Console.WriteLine(System.Object.ReferenceEquals(i, (int)obj)); Console.WriteLine(System.Object.ReferenceEquals(i, (int)ftt));
上面代碼輸出如下,至於發生多少次裝箱多少次拆箱,你猜?
False False False False False
版權所有,文章來源:http://hovertree.com
個人能力有限,本文內容僅供學習、探討,歡迎指正、交流。
書籍:CLR via C#
書籍:你必須知道的.NET
1.4.2 裝箱和拆箱