程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C# 裝箱 (boxing) 和拆箱 (unboxing),

C# 裝箱 (boxing) 和拆箱 (unboxing),

編輯:C#入門知識

C# 裝箱 (boxing) 和拆箱 (unboxing),


目錄:

 

1. 裝箱和拆箱

2. 深入理解裝箱和拆箱

3. int[] to object[],值類型數組到對象數組的轉化

4. 使用泛型減少裝箱和拆箱

1.  裝箱和拆箱

 

裝箱 就是把“值類型”轉換成“引用類型”;

拆箱 就是把“引用類型”轉換成“值類型”;

        首先,我們要弄明白為什麼需要裝箱和拆箱。C#的所有類型,包括int、boo等,都繼承自System.Object,但是卻又有值類型和引用類型之分。這時你要問,int是繼承自object類型的,object是引用類型,那為何int不是引用類型而是值類型的呢?這就涉及到裝箱和拆箱的概念了。

         我們知道對象是創建在堆上的,它的創建和銷毀必然帶來額外的CPU和內存消耗。如果將int,boo等微小而常用的數據類型都放在堆上創建和銷毀,語言的性能將會被極大的限制,有時甚至是無法忍受的。C#將值類型和引用類型分開,值類型直接在棧中被創建,超過作用域後直接銷毀。當需要值類型成為對象時,使用裝箱操作,讓值類型變為一個引用類型的對象。這樣,我們就可以使用object作為通用的接口統一語言內的一切類型。

        拆箱 在MSDN官方文檔裡用的是 取消裝箱。事實上拆箱是裝箱的逆操作,也就是說我們只對裝過箱的引用類型(通常是object對象)進行拆箱操作。單純拆箱操作的後果無法設想的。

        裝箱和拆箱是C#的核心概念,C#利用其完成類型系統的統一。有了裝箱,任何類型的值都可以視為一個對象。CLR在裝箱時是將值類型包裝到System.Object的內部,再將其存儲到托管堆上。拆箱是從對象中提取值類型。裝箱是隱式的而拆箱是顯示的。

//裝箱 boxing
int i = 3 ;  //分配在棧上
object o = i ;//隱式裝箱操作,int i 在堆上
object b = (object)i ; //顯示裝箱操作
//拆箱 unboxing
int j = (int) o ;//顯示拆箱(將對象o拆箱為int類型)

int k = b ;//error!!, 不能隱式拆箱

拆箱 的操作包括

1,檢查對象實例,以卻確保它是給定值類型的裝箱值。

2,將該值從實例復制到值類型變量中。

 

下面來看看這個例子:

int i=0;
System.Object obj=i;
Console.WriteLine(i+","+(int)obj);

其中共發生了3次裝箱和一次拆箱!^_^,看出來了吧?!
第一次是將i裝箱,第2次是輸出的時候將i轉換成string類型,而string類型為引用類型,即又是裝箱,第三次裝箱就是(int)obj的轉換成string類型,裝箱!
拆箱就是(int)obj,將obj拆箱!!

2.  深入理解裝箱和拆箱

object o = 1 ;

這句話的IL代碼如下:

.locals init (

  [0] object objValue

  ) //以上三行IL表示聲明object類型的名稱為objValue的局部變量

  IL_0000: nop

  IL_0001: ldc.i4.s 1 //表示將整型數1放到棧頂

  IL_0003: box [mscorlib]System.Int32 //執行IL box指令,在內存堆中申請System.Int32類型需要的堆空間

  IL_0008: stloc.0 //彈出堆棧上的變量,將它存儲到索引為0的局部變量中

 

注意注釋的部分。執行裝箱操作時不可避免的要在堆上申請內存空間,並將堆棧上的值類型數據復制到申請的堆內存空間上,這肯定是要消耗內存和cpu資源的。

object objValue = 4;

int value = (int)objValue;

同樣,看看IL代碼:

.locals init (

  [0] object objValue,

  [1] int32 'value'

  ) //上面IL聲明兩個局部變量object類型的objValue和int32類型的value變量

  IL_0000: nop

  IL_0001: ldc.i4.4 //將整型數字4壓入棧

  IL_0002: box [mscorlib]System.Int32 //執行IL box指令,在內存堆中申請System.Int32類型需要的堆空間

  IL_0007: stloc.0 //彈出堆棧上的變量,將它存儲到索引為0的局部變量中

  IL_0008: ldloc.0//將索引為0的局部變量(即objValue變量)壓入棧

  IL_0009: unbox.any [mscorlib]System.Int32 //執行IL 拆箱指令unbox.any 將引用類型object轉換成System.Int32類型

  IL_000e: stloc.1 //將棧上的數據存儲到索引為1的局部變量即value

 

拆箱操作的執行過程和裝箱操作過程正好相反,是將存儲在堆上的引用類型值轉換為值類型並給值類型變量。裝箱操作和拆箱操作是要額外耗費cpu和內存資源的,所以在c# 2.0之後引入了泛型來減少裝箱操作和拆箱操作消耗。

3. int[] to object[],值類型數組到對象數組的轉化

我們不能直接把值類型的數組賦值給對象數組,例如:

 int[] array  = new int[] { 0 } ;
 object[] oiArray = (object[])array;//error!! 不能將int[] 轉換到 object[]
 string[] a={"1","2","3"};
 object[] osArray = a ;//正確,a是引用類型數組,不存在裝箱和拆箱

(object[])a無法將a所有的值類型對象“直接”轉換為引用類型,所以編譯器不會通過這個轉換。可以使用如下的方式達到目的:

  int[] array  = new int[] { 0 } ;
  object[] oArray = new object[array.Length];
  for(int i =0 ; i< array.Length ; i++)
  {
     oArray[i] = array[i]; //隱式裝箱
  }

 

4. 使用泛型減少裝箱和拆箱

        有時說使用泛型能提高C#程序的性能,有一部分性能的提升是由減少了裝箱和拆箱帶來的。考察下面的代碼:

public class Test
{
    object _o  ;
    
    public Test(object o)
    {
	_o = o ;
    }
}


public class Test<T>
{
    T _o ;
    public Test(T o)
    {
        _o = o ;
    }
}

        第一個Test類中沒有使用泛型,如果將一個int類型的值傳入Test,將會引發多次的裝箱和拆箱操作。而泛型類在實例化時已經明確了類型,復制操作時就不會有裝箱和拆箱操作了。

 

引用:

1. 玉開 http://www.cnblogs.com/yukaizhao/archive/2011/10/18/csharp_box_unbox_1.html

2. MSDN https://msdn.microsoft.com/zh-cn/library/yz2be5wk.aspx

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved