C#裝箱與拆箱
要掌握裝箱與拆箱,就必須瞭解CTS及它的特點。
NET重要技術和基礎之一的CTS(Common Type System)。顧名思義,CTS就是為了實現在應用程式聲明和使用這些類型時必須遵循的規則而存在的一般型別系統。.Net將整個系統的類型分成兩大類 ——Value Type 和 Reference Type。。,多數的OO語言存在這個弱點,原因就是因為他們的原類型沒有共同的基點,於是他們在本質上並不是真正的對象C++更依賴於對象,而非物件導向。.Net環境的CTS 給我們帶來了方便。第一、CTS中的所有東西都是對象;第二、所有的對象都源自一個基類——System.Object類型。這就是所謂的單根階層(singly rooted hierarchy)關於System.Object的詳細資料請參考微軟的技術文檔。CTS Value Type的一個最大的特點是它們不能為null,Value Type的變數總有一個值。在傳遞Value Type的變數時,實際傳遞的是變數的值,而非底層對象的“引用”。CTS Reference Type就好像是型別安全的指標,它可以為null。當值為null時,說明沒有引用或類型指向某個對象。聲明一個參考型別的變數時,被操作的是此變數的引用(地址),而不是資料。
使用這種多類型系統時如何有效拓展和提高系統的效能?就是今天探討的問題,西雅圖人提出了Box and UnBox的想法。簡言之,裝箱就是將value type轉換為reference type;反之,就是拆箱。
裝箱過程:
第一步將一個值壓入堆棧;
第二步將參考型別轉換為實值型別;
第三步間接將值壓棧;第四步傳值給dubUnBox。
代碼如下:
using System; namespace Box { /// /// BoxAndUnBox 的摘要說明。 /// public class BoxAndUnBox { public BoxAndUnBox() { // // TODO: 在此處添加建構函式邏輯 // } ///////////////////////////////////////////////////////////////////////////////////// static void Main(string[] args) { double box1 =11.222; /// 定義一個值形變數 object objBox =box1; /// 將變數的值裝箱到 一個引用型對象中 Console.WriteLine("The Value is '{0}' and The Boxed is {1}",box1,objBox.ToString()); }
} }
開啟ildasm.exe
MSIL代碼如下:
.method private hidebysig static void Main(string[] args) cil managed { .entrypoint // 代碼大小 42 (0x2a) .maxstack 3 .locals init ([0] float64 box1, [1] object objBox) IL_0000: nop
IL_0001: ldc.r8 11.222 IL_000a: stloc.0 //第IL_0000至IL_000a是定義值型變數的 IL_000b: ldloc.0 IL_000c: box [mscorlib]System.Double IL_0011: stloc.1 //第IL_000b 至 IL_0011 行是描述object objBox =box1代碼的 IL_0012: ldstr "The Value is '{0}' and The UnBoxed is {1}" IL_0017: stloc.0 IL_0018: box [mscorlib]System.Double IL_001d: stloc.1 IL_001e: callvirt instance string [mscorlib]System.Object::ToString()
IL_0023: call void [mscorlib]System.Console::WriteLine(string, object, object)
IL_0028: nop IL_0029: ret } // end of method BoxAndUnBox::Main
當box1被裝箱時所發生的過程:
(1)劃分堆棧記憶體,在堆棧上分配的記憶體 = box1的大小 + objBox及其結構所佔用的空間;(2) box1的值(11.222)被複製到新近分配的堆棧中;
(3)將分配給objBox的地址壓棧,此時它指向一個object類型,即參考型別。
拆箱過程:
裝箱的逆過程。值得注意以下幾點:box time不需要顯式的類型轉換,在unbox時就必須進行類型轉換。因為參考型別的對象可以被轉換為任何類型。電腦和人腦一個差別的體現就在於此!哈哈!類型轉換不容迴避的將會受到來自CTS管理中心的監控——其標準自然是依據規則。
下面這段代碼:
using System; namespace UnBox { /// /// BoxAndUnBox 的摘要說明。 /// public class BoxAndUnBox { public BoxAndUnBox() { // // TODO: 在此處添加建構函式邏輯 // } ///////////////////////////////////////////////////////////////////////////////////// static void Main(string[] args) { double box2 = 11.222; object objBox = box2; double dubUnBox = (double)objBox; /// 將引用型對象拆箱 ,並傳回值 Console.WriteLine("The Value is '{0}' and The UnBoxed is {1}",box2,dubUnBox); } ///////////////////////////////////////////////////////////////////////////////////// } }
本段代碼多加了一行double dubUnBox = (double)objBox;
這段代碼的含義:
第一步將一個值壓入堆棧;
第二步將參考型別轉換為實值型別;
第三步間接將值壓棧;
第四步傳值給dubUnBox。
.method private hidebysig static void Main(string[] args) cil managed { .entrypoint // 代碼大小 48 (0x30) .maxstack 3 .locals init ([0] float64 box1, [1] object objBox, [2] float64 dubUnBox) IL_0000: ldc.r8 77.769999999999996 IL_0009: stloc.0 IL_000a: ldloc.0 IL_000b: box [mscorlib]System.Double IL_0010: stloc.1 IL_0011: ldloc.1 IL_0012: unbox [mscorlib]System.Double IL_0017: ldind.r8 IL_0018: stloc.2 IL_0019: ldstr "The Value is '{0}' and The UnBoxed is {1}" IL_001e: ldloc.0 IL_001f: box [mscorlib]System.Double IL_0024: ldloc.2 IL_0025: box [mscorlib]System.Double IL_002a: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_002f: ret } // end of method BoxAndUnBox::Main
//
第IL_0011 至 IL_0018 行是描述double dubUnBox = (double)objBox代碼的。
描述一下objBox在拆箱時的情況:(1)環境須先判斷堆棧上指向合法對象的地址,以及在對此對象向指定的類型進行轉換時是否合法,如果不合法,就拋出異常;(2)當判斷類型轉換正確,就返回一個指向對象內的值的指標。
改進:
為了避免由於無謂的隱式裝箱所造成的效能損失,在執行這些多類型重載方法之前,最好先對值進行裝箱。
代碼改進:
using System; namespace NewBU { /// /// BoxAndUnBox 的摘要說明。 /// public class BoxAndUnBox { public BoxAndUnBox() { // // TODO: 在此處添加建構函式邏輯 // } /////////////////////////////////////////////////////////////////// static void Main(string[] args) { double box1 = 11.222; object objBox = box1; double dubUnBox = (double)objBox; object objUnBox = dubUnBox; Console.WriteLine("The Value is '{0}' and The UnBoxed is {1}",objBox,objUnBox); } /////////////////////////////////////////////////////////////////// } }
C#裝箱與拆箱
要掌握裝箱與拆箱,就必須瞭解CTS及它的特點。
NET重要技術和基礎之一的CTS(Common Type System)。顧名思義,CTS就是為了實現在應用程式聲明和使用這些類型時必須遵循的規則而存在的一般型別系統。.Net將整個系統的類型分成兩大類 ——Value Type 和 Reference Type。。,多數的OO語言存在這個弱點,原因就是因為他們的原類型沒有共同的基點,於是他們在本質上並不是真正的對象C++更依賴於對象,而非物件導向。.Net環境的CTS 給我們帶來了方便。第一、CTS中的所有東西都是對象;第二、所有的對象都源自一個基類——System.Object類型。這就是所謂的單根階層(singly rooted hierarchy)關於System.Object的詳細資料請參考微軟的技術文檔。CTS Value Type的一個最大的特點是它們不能為null,Value Type的變數總有一個值。在傳遞Value Type的變數時,實際傳遞的是變數的值,而非底層對象的“引用”。CTS Reference Type就好像是型別安全的指標,它可以為null。當值為null時,說明沒有引用或類型指向某個對象。聲明一個參考型別的變數時,被操作的是此變數的引用(地址),而不是資料。
使用這種多類型系統時如何有效拓展和提高系統的效能?就是今天探討的問題,西雅圖人提出了Box and UnBox的想法。簡言之,裝箱就是將value type轉換為reference type;反之,就是拆箱。
裝箱過程:
第一步將一個值壓入堆棧;
第二步將參考型別轉換為實值型別;
第三步間接將值壓棧;第四步傳值給dubUnBox。
代碼如下:
using System; namespace Box { /// /// BoxAndUnBox 的摘要說明。 /// public class BoxAndUnBox { public BoxAndUnBox() { // // TODO: 在此處添加建構函式邏輯 // } ///////////////////////////////////////////////////////////////////////////////////// static void Main(string[] args) { double box1 =11.222; /// 定義一個值形變數 object objBox =box1; /// 將變數的值裝箱到 一個引用型對象中 Console.WriteLine("The Value is '{0}' and The Boxed is {1}",box1,objBox.ToString()); }
} }
開啟ildasm.exe
MSIL代碼如下:
.method private hidebysig static void Main(string[] args) cil managed { .entrypoint // 代碼大小 42 (0x2a) .maxstack 3 .locals init ([0] float64 box1, [1] object objBox) IL_0000: nop
IL_0001: ldc.r8 11.222 IL_000a: stloc.0 //第IL_0000至IL_000a是定義值型變數的 IL_000b: ldloc.0 IL_000c: box [mscorlib]System.Double IL_0011: stloc.1 //第IL_000b 至 IL_0011 行是描述object objBox =box1代碼的 IL_0012: ldstr "The Value is '{0}' and The UnBoxed is {1}" IL_0017: stloc.0 IL_0018: box [mscorlib]System.Double IL_001d: stloc.1 IL_001e: callvirt instance string [mscorlib]System.Object::ToString()
IL_0023: call void [mscorlib]System.Console::WriteLine(string, object, object)
IL_0028: nop IL_0029: ret } // end of method BoxAndUnBox::Main
當box1被裝箱時所發生的過程:
(1)劃分堆棧記憶體,在堆棧上分配的記憶體 = box1的大小 + objBox及其結構所佔用的空間;(2) box1的值(11.222)被複製到新近分配的堆棧中;
(3)將分配給objBox的地址壓棧,此時它指向一個object類型,即參考型別。
拆箱過程:
裝箱的逆過程。值得注意以下幾點:box time不需要顯式的類型轉換,在unbox時就必須進行類型轉換。因為參考型別的對象可以被轉換為任何類型。電腦和人腦一個差別的體現就在於此!哈哈!類型轉換不容迴避的將會受到來自CTS管理中心的監控——其標準自然是依據規則。
下面這段代碼:
using System; namespace UnBox { /// /// BoxAndUnBox 的摘要說明。 /// public class BoxAndUnBox { public BoxAndUnBox() { // // TODO: 在此處添加建構函式邏輯 // } ///////////////////////////////////////////////////////////////////////////////////// static void Main(string[] args) { double box2 = 11.222; object objBox = box2; double dubUnBox = (double)objBox; /// 將引用型對象拆箱 ,並傳回值 Console.WriteLine("The Value is '{0}' and The UnBoxed is {1}",box2,dubUnBox); } ///////////////////////////////////////////////////////////////////////////////////// } }
本段代碼多加了一行double dubUnBox = (double)objBox;
這段代碼的含義:
第一步將一個值壓入堆棧;
第二步將參考型別轉換為實值型別;
第三步間接將值壓棧;
第四步傳值給dubUnBox。
.method private hidebysig static void Main(string[] args) cil managed { .entrypoint // 代碼大小 48 (0x30) .maxstack 3 .locals init ([0] float64 box1, [1] object objBox, [2] float64 dubUnBox) IL_0000: ldc.r8 77.769999999999996 IL_0009: stloc.0 IL_000a: ldloc.0 IL_000b: box [mscorlib]System.Double IL_0010: stloc.1 IL_0011: ldloc.1 IL_0012: unbox [mscorlib]System.Double IL_0017: ldind.r8 IL_0018: stloc.2 IL_0019: ldstr "The Value is '{0}' and The UnBoxed is {1}" IL_001e: ldloc.0 IL_001f: box [mscorlib]System.Double IL_0024: ldloc.2 IL_0025: box [mscorlib]System.Double IL_002a: call void [mscorlib]System.Console::WriteLine(string, object, object) IL_002f: ret } // end of method BoxAndUnBox::Main
//
第IL_0011 至 IL_0018 行是描述double dubUnBox = (double)objBox代碼的。
描述一下objBox在拆箱時的情況:(1)環境須先判斷堆棧上指向合法對象的地址,以及在對此對象向指定的類型進行轉換時是否合法,如果不合法,就拋出異常;(2)當判斷類型轉換正確,就返回一個指向對象內的值的指標。
改進:
為了避免由於無謂的隱式裝箱所造成的效能損失,在執行這些多類型重載方法之前,最好先對值進行裝箱。
代碼改進:
using System; namespace NewBU { /// /// BoxAndUnBox 的摘要說明。 /// public class BoxAndUnBox { public BoxAndUnBox() { // // TODO: 在此處添加建構函式邏輯 // } /////////////////////////////////////////////////////////////////// static void Main(string[] args) { double box1 = 11.222; object objBox = box1; double dubUnBox = (double)objBox; object objUnBox = dubUnBox; Console.WriteLine("The Value is '{0}' and The UnBoxed is {1}",objBox,objUnBox); } /////////////////////////////////////////////////////////////////// } }