# [.NET] 什麼是 Boxing 和 Unboxing ? ###### tags: `Bolger` `.NET` `C#` ## [.NET] 什麼是 Boxing 和 Unboxing ? ### Boxing Boxing 將儲存 **堆疊(stack)** 的 **實值類型(Value type)** 轉換為 object 類型,此轉換為 **隱含轉換(implicit conversion)** 。 - Boxing 處理時,會在 **堆積(heap)** 產生新物件並將值複製過去。 - 用實值型別實作的任何介面類型也適用。 有點類似實務的例如你有一個東西(錢包),把它裝箱並放置到某個抽屜,這個抽屜有把鑰匙讓你知道放在哪個抽屜 <center>  </center> ``` C# int i = 123; // Boxing object o = i; ``` 透過[sharplab][sharplab]反編譯出來的IL ([介紹sharplab][介紹sharplab]) ``` IL IL_0000: nop IL_0001: ldc.i4.s 123 IL_0003: stloc.0 IL_0004: ldloc.0 IL_0005: box [System.Private.CoreLib]System.Int32 // Boxing IL_000a: stloc.1 IL_000b: ret ``` ### Unboxing **Unboxing** 就是 Boxing 的相反,將 object 類型明確轉換為實值型別。 - 注意 object 是否為 Null 如果是會造成 `NullReferenceException` - 嘗試不相容的實值類型進行 Unboxing 會造成 `InvalidCastException` <center>  </center> ``` C# object o = 123; // Unboxing int i = (int)o; ``` ``` IL IL_0000: nop IL_0001: ldc.i4.s 123 IL_0003: box [System.Private.CoreLib]System.Int32 IL_0008: stloc.0 IL_0009: ldloc.0 IL_000a: unbox.any [System.Private.CoreLib]System.Int32 // Unboxing IL_000f: stloc.1 IL_0010: ret ``` ### Boxing and Unboxing 效能測試 ``` C# private static void WithoutBox(object obj) { int i = 123; } private static void Boxing(object obj) { object i = 123; } private static void UnBoxing(object obj) { int i = (int)obj; } ``` ``` IL # WithoutBox IL_0000: nop IL_0001: ldc.i4.s 123 IL_0003: stloc.0 IL_0004: ret # Boxing IL_0000: nop IL_0001: ldc.i4.s 123 IL_0003: box [System.Private.CoreLib]System.Int32 IL_0008: stloc.0 IL_0009: ret # UnBoxing IL_0000: nop IL_0001: ldarg.0 IL_0002: unbox.any [System.Private.CoreLib]System.Int32 IL_0007: stloc.0 IL_0008: ret ``` 測試100,000,00後可以在處理 **Boxing** 的耗時最久幾乎是沒有任何處理快兩倍的時間,**UnBoxing** 也是比較久所以在寫程式上盡量避免 Boxing 或 UnBoxing 產生。 這裡是[範例][gitlab],如有理解錯誤歡迎大家多多指教,謝謝 ``` cmd WithoutBox Test 耗時 381 毫秒 Boxing Test 耗時 728 毫秒 UnBoxing Test 耗時 455 毫秒 ``` ### Boxing 範例 範例 1: 不小心就 Boxing 通常我們在寫 Log 時候,很常在寫 Log 訊息使用字串簡化寫法,但其實不小心就造成 Boxing 產生,此種狀況可能造成效率問題。 ```C# var ex1 = 123; Console.WriteLine($" {ex1}"); // Boxing Console.WriteLine($" {ex1.ToString()}"); ``` ```IL IL_0000: nop IL_0001: ldc.i4.s 123 IL_0003: stloc.0 IL_0004: ldstr " {0}" IL_0009: ldloc.0 IL_000a: box [System.Private.CoreLib]System.Int32 // Boxing IL_000f: call string [System.Private.CoreLib]System.String::Format(string, object) IL_0014: call void [System.Console]System.Console::WriteLine(string) IL_0019: nop IL_001a: ldstr " " IL_001f: ldloca.s 0 IL_0021: call instance string [System.Private.CoreLib]System.Int32::ToString() IL_0026: call string [System.Private.CoreLib]System.String::Concat(string, string) IL_002b: call void [System.Console]System.Console::WriteLine(string) IL_0030: nop IL_0031: ret ``` 範例 2: 容易誤會有 Boxing 重點:Boxing 只有在執行 **實值型別(Value type)** , string 是 參考型別所以不會產生 Boxing。 ([參考][reference types]) ```C# var ex2 = "123"; object obj2 = ex2; obj2 = "456"; Console.WriteLine($"Example2 string result: {ex2}"); Console.WriteLine($"Example2 object result: {obj2}"); ``` ```IL IL_000f: ldstr "Example2 string result: " IL_0014: ldloc.0 IL_0015: call string [System.Private.CoreLib]System.String::Concat(string, string) IL_001a: call void [System.Console]System.Console::WriteLine(string) IL_001f: nop IL_0020: ldstr "Example2 object result: {0}" IL_0025: ldloc.1 IL_0026: call string [System.Private.CoreLib]System.String::Format(string, object) IL_002b: call void [System.Console]System.Console::WriteLine(string) ``` ### 參考資料 [Boxing 和 Unboxing][boxing-and-unboxing] [boxing 與 unboxing 重點整理與程式範例][bloger-boxing-and-unboxing] [Why do we need boxing and unboxing][why-need] [Object type boxing with a reference type variable][boxing-with-a-reference-type-variable] [Reference types][reference types] <!-- 路徑 --> [sharplab]: https://sharplab.io/ [介紹sharplab]: https://putaonini.gitlab.io/tool/20211030/3902068567/ [gitlab]: https://gitlab.com/PuTaoNiNi/csharpsample/-/blob/main/BoxingAndUnboxing/Program.cs [boxing-and-unboxing]: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/types/boxing-and-unboxing [why-need]: https://stackoverflow.com/questions/2111857/why-do-we-need-boxing-and-unboxing-in-c [bloger-boxing-and-unboxing]: https://nwpie.blogspot.com/2017/04/5-boxing-unboxing.html [reference types]: https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/reference-types <!-- reference types => boxing => object types 是否會再產生一組, 還是指標過去 --> [boxing-with-a-reference-type-variable]: https://stackoverflow.com/questions/9092961/object-type-boxing-with-a-reference-type-variable [Is object a reference type or value type]: https://stackoverflow.com/questions/17673029/is-object-a-reference-type-or-value-type <!-- 之前的問題可以補一下 --> [問題]: https://www.facebook.com/groups/DotNetUserGroupTaiwan/permalink/2571258316500604/
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up