【c#】裝箱與拆箱

  從內存執行的角度來看,值類型的內存分配在線程的堆棧上,而引用類型的內存分配在託管堆上。所以從值類型向引用類型的轉換,勢必牽涉到數據的拷貝與指針引用等操做。c#

  裝箱操做,大體過程爲:在託管堆中分配新對象的內存,將值類型的字段拷貝到該內存中,而後返回該對象的地址,這樣就完成了從值類型到引用類型的轉變;拆箱操做,則是獲取已裝箱對象中來自值類型部分字段的地址。裝箱與拆箱並不是徹底對稱的互逆操做,拆箱並不包含字段的拷貝。數據結構

  概念雷區:性能

  1. 裝箱與拆箱不是徹底對等的互逆操做。從內存的角度上看,拆箱的性能開銷遠小於裝箱,只是在實際的執行中,拆箱以後常伴隨着字段的拷貝,以c#爲例,編譯器老是自動產生拆箱以後的字段拷貝。
  2. 只有被裝箱過的對象才能被拆箱,非全部的引用類型。將非裝箱而來的引用類型強制轉換爲值類型,會拋出InvalidCastException異常。

分拆

  值類型,提供了輕量型的數據結構,具備較少的內存開銷,對系統性能有明顯的做用。而缺點是:缺省方法表指針,由於沒法在指望System.Object或其繼承類的方法上調用值類型。spa

  裝箱過程解析線程

  1. 內存分配:在託管堆中分配內存空間,內存大小爲欲裝箱值類型的大小加上其餘額外的內存空間,主要包括方法表指針與SyncBlockIndex,此兩個成員用於CLR管理引用類型對象。
  2. 實例拷貝:將值類型的字段拷貝到新分配的內存中去
  3. 地址返回

  拆箱過程解析指針

  1. 實例檢查:首先檢查是不是null,如果拋出NullReferenceException;若非,檢查對象實例,確保是給定值類型的裝箱值,而且保證拆箱後的類型爲原來的同一類型,不然會拋出InvalidCastException
  2. 指針返回:返回已經裝箱對象中屬於原值類型部分字段的地址。而附加成員:方法表指針與SyncBlockIndex對該指針是不可見的。
  3. 字段拷貝:將託管堆中實例的字段拷貝到線程的堆棧中。

性能

  1. 在實際的項目中留意發生隱式裝箱的可能,並提供相應的多個重載方法來避免裝箱的發生。
  2. 裝箱與拆箱常常是以隱式發生的,在系統中顯式的實現裝箱操做,是提升性能的較好選擇
  3. 泛型能有效減小了裝箱與拆箱的發生,大大提升了系統的性能與穩定。

應用

  1. ArrayList與Array
  2. Hashtable
  3. 枚舉
    枚舉類型爲典型的.Net值類型,能夠被裝箱爲System.Object,System.ValueType和System.Enum,以及System.Enum實現的三個接口類型System.IComparable,System.IConvertible,System.IFormattable
  4. 關注不經意的隱式轉換
     1 public static void Main()
     2 {
     3     int i = 100;
     4     //裝箱
     5     i.GetType();
     6     //未裝箱
     7     i.ToString();
     8     //顯式裝箱
     9     object o = i;
    10     Hashtable ht = new Hashtable();
    11     ht.Add("One", o);
    12     ht.Add("Two", o);
    13 }

    GetType方法由System.Object類型提供,所以值類型調用時必須執行裝箱操做;而ToString方法則由int類型覆蓋,所以不會裝箱。Hashtable的Add方法接受System.Object類型的參數,所以經過顯式的類型轉換來減小隱式的裝箱操做。code

相關文章
相關標籤/搜索