[ASP.NET] Boxing(裝箱) 與 UnBoxing(拆箱) 測試

介紹 Boxing 與 UnBoxing 發生的情況與測試有 Boxing 與 無 Boxing 的效能。

前言


  一般來說在程式碼撰寫的過程中,可能不太會注意到 Boxing 與 UnBoxing 的問題,尤其是在專案比較小的情況下,如果使用的人數並不多就更少會注意到,Boxing 與  UnBoxing 是什麼? 且會對網站的運作造成何種影響呢?

 

  當網站發生 Boxing 與 UnBoxing 的情況時,主要會影響到網站的「效能」,在 MSDN 文件中提到「Box 和 Unbox 處理是會耗費大量運算資源的處理序」,接下來看先來瞭解 Boxing 與 UnBoxing 是如何發生的。

 

Boxing 與 UnBoxing


  在 .NET 中將型別區分為 Value Type (實質型別) 與 Reference Type (參考型別) ,當產生了一個 Value Type 的變數時,會將此 Value Type 變數值存放於 Stack (堆疊) 中,而如產生的是一個 Reference Type 物件時會將此物件值存放於 Heap (堆積) 中,在發生 Boxing 處理時是因為將實質型別轉換成 object 型別。

 

  參考 MSDN 範例,首先產生一個 Value Type 變數 i。


int i = 123;

 

  接下產生一個 object o 物件,將 i 轉換為 o 物件,此時 i 轉換 o 物件時是將 i 變數的值複製一份到 o 物件 Heap 裡的空間,在 i 轉換成 o 時則是進行了隱含式的 Boxing 作業。


object o = i;

 

  另外參考 MSDN 中的圖示,如下:

 

  而 UnBoxing 處理則為將 object 型別明確轉換成實質型別時發生,如下程式碼在將 o 物件使用明確轉型式轉換成 j 變數後,則會將 o 物件的值複製一份到 j 變數的 Stack 中。


int i = 123; // value type
object o = i; // boxing
int j = (int)o; // unboxing

 

  另外參考 MSDN 中的圖示,如下:

 

  接下來我們透過 IL 反組譯工具來看一下反組譯後的編碼,就可以看到在紅色框框中的進行轉換時產生了 box 與 unbox 處理,如下。

 

 

測試 Boxing 與 UnBoxing


  在瞭解了 Boxing 與 UnBoxing 的發生原因後就來進行個測試,一開始有提到在 Box 與 UnBox 時會消耗效能,接下來就使用以下測試程式碼進行測試看看。

 

  舉一個常常發生 Boxing 的情況,就是在使用 string.Format() 方法時,在指定好格式時需要傳入對應的參數,此時如果參數有包含 int 型別的變數時,直接傳入並不會發生錯誤,因為會自動替你轉換,但是如果直接將 int 型別變數直接使用而未使用 ToString() 方法先進行格式化為字串時就會發生 Boxing 處理,使用以下程式碼進行測試。


private void HasBoxing()
{
    Stopwatch stopWatch = new Stopwatch();
    stopWatch.Start();

    string source = "";
    for (int i = 0; i < 10000000; i++)
    {
        source = string.Format("Number{0},{1},{2}", i, i, i);
    }

    stopWatch.Stop();
    TimeSpan span = stopWatch.Elapsed;
    Response.Write(string.Format("Boxing Test 耗時 {0}毫秒<br>", span.Milliseconds.ToString()));
}

private void NoBoxing()
{
    Stopwatch stopWatch = new Stopwatch();
    stopWatch.Start();

    string source = "";
    for (int i = 0; i < 10000000; i++)
    {
        source = string.Format("Number{0},{1},{2}", i.ToString(), i.ToString(), i.ToString());
    }

    stopWatch.Stop();
    TimeSpan span = stopWatch.Elapsed;
    Response.Write(string.Format("No Boxing Test 耗時 {0}毫秒<br>", span.Milliseconds.ToString()));
}

 

  在以上兩段程式碼進行呼叫後,產生的結果如下圖。

 

  從上圖中可以明顯發現,在進行了 10000000 次的迴圈計算後,進行 Boxing 處理的方法耗時 874 毫秒,而使用 ToString() 的方法耗時 155 毫秒,明顯可以發現在進行 Boxing 時的效能消耗,如發生在大流量的網站上將會更加明顯,由此可見當在進行型別轉換時應盡量避免發生 Boxing 與 UnBoxing 的情況,才能有效提升網站效能。

 

參考資料


Boxing 和 Unboxing (C# 程式設計手冊)

Boxing and UnBoxing in C#

《Effective C#》Item 17:减少装箱(Boxing)和拆箱(Unboxing)操作

[ASP.NET] ToString() ? Boxing ?

 

 


以上文章敘述如有錯誤及觀念不正確,請不吝嗇指教
如有侵權內容也請您與我反應~謝謝您 :)