裝拆箱

裝箱(boxing)和拆箱(unboxing)是C#類型系統的核心概念。html

值類型繼承至System.ValueType,引用類型繼承至System.Object,詳細的類繼承關係以下圖所示:dom

 

值類型實例一般分配在線程的棧(stack)上,而且不包含任何指向實例數據的指針,由於變量自己就包含了其實例數據,而引用類型實例分配在託管堆(managed heap)上,變量保存了實例數據的內存引用.棧是一種先進後出,而且由系統自動操做的存儲空間,而堆(在.NET上準確的說是託管堆Managed Heap)是一種自由儲存區(Free Memory),在該區域中,必須明確的爲對象申請存儲空間(使用的new關鍵字):示例:
 public void Method() 

{ 

int x=3;  //第一行

int y=5;  //第二行

class cls1 = new class();  //第三行

}

下面逐行分析以上三行代碼的執行過程。ide

1行:當這行代碼執行時,編譯器爲它分配一小塊棧內存,運行時棧負責提供程序所需的內存。性能

2行:棧在第一塊內存的頂部分配了另外一塊內存。注意,內存的分配與釋放遵循後進先出邏輯。spa

3行:編譯器在棧上建立了一個指針,真實的對象存儲在託管堆中。託管堆並不跟蹤運行內存,它更像一堆隨時能夠訪問的對象。託管堆上分配的內存大小由具體對象的大小而決定。pwa

 

不扯了,說下裝拆箱:線程

一、裝箱就是將一個值類型轉換成等價的引用類型。 它的過程分爲這樣幾步:
1) 在託管堆上分配一個新的對象。
2) 將棧上的數據轉移到託管堆新建立的對象空間上。
3) 在棧上分配一個指向託管堆的引用。
二、拆箱則是將一個已裝箱的引用類型轉換爲值類型:
1)將託管堆上對象的數據轉移到棧上
2) 垃圾回收器(GC)回收託管堆上的內存空間。
3)刪除棧上分配的對象引用。
 
1             int n = 100;
2             //有沒有發生裝箱?沒有,只是調用方法.查看IL沒有box,unbox關鍵字
3             string s = Convert.ToString(n);
4             int m = int.Parse(s);
5             Console.ReadKey();
View Code

裝拆箱:3d

1             int n = 100;
2             object o = n;//發生了一次裝箱
3             int m = (int)o; //發生了一次拆箱
4             Console.WriteLine(m);
5             Console.ReadKey();
View Code

查看IL代碼以下:指針

注意問題:裝箱的時候是什麼類型,拆箱的時候也必須使用對應的類型拆箱。不然會報錯code

=============================================================================================

以前寫了一篇比較是否爲同一對象(比較是否爲同一對象),object.ReferenceEquals若是兩個參數都是值類型會是怎樣的呢?

 

1            int n = 100;
2             int m = 100;
3             Console.WriteLine(n.Equals(m));
4             Console.WriteLine(object.ReferenceEquals(n, m));
5             Console.ReadKey();
View Code

 

運行結果是false,咱們來查看下IL代碼,很明顯,n和m都裝箱變爲了引用類型,它們所指向的地址不同,不管如何都是不成立的

 

=================================================================================

拼接字符串也會涉及裝箱問題:好比:

1             int n = 10;
2             object o = n; //裝箱一次
3             n = 100;
4             Console.WriteLine(n + "," + (int)o); //裝箱兩次,拆箱一次
5             Console.ReadKey();
View Code

仍是得查看一下IL代碼:由於拼接字符串是調用string.Concat(object,object,object)這個方法,每拼接一個字符串會把int隱式裝箱再鏈接

======================================================================

裝箱和拆箱性能問題

使用ArrayList:

 1             ArrayList arrInt = new ArrayList();
 2             Stopwatch watch = new Stopwatch();
 3             watch.Start();
 4             for (int i = 0; i < 1000000; i++)
 5             {
 6                 arrInt.Add(i);
 7             }
 8             watch.Stop();
 9             Console.WriteLine(watch.Elapsed);
10             Console.ReadKey();
View Code

運行結果:

使用泛型:

 1     //使用泛型集合避免裝箱和拆箱。
 2             List<int> arrInt = new List<int>();
 3             Stopwatch watch = new Stopwatch();
 4             watch.Start();
 5             for (int i = 0; i < 1000000; i++)
 6             {
 7                 arrInt.Add(i);
 8             }
 9             watch.Stop();
10             Console.WriteLine(watch.Elapsed);
11             Console.ReadKey();
View Code

運行結果:

這能夠看出裝拆箱仍是比較影響性能的

相關文章
相關標籤/搜索