裝箱與拆箱

来源:http://www.cnblogs.com/yankun1991/archive/2017/06/13/7001354.html
-Advertisement-
Play Games

裝箱: 值類型比引用類型“輕”,原因是他們不作為對象在托管堆中分配,不被垃圾回收,也不通過指針進行引用。但是許多時候都需要獲取值類型的引用,例如,假定要創建ArrayList對象來容納一組point結構,代碼如下: public sealed class Program { public stati ...


裝箱: 值類型比引用類型“輕”,原因是他們不作為對象在托管堆中分配,不被垃圾回收,也不通過指針進行引用。但是許多時候都需要獲取值類型的引用,例如,假定要創建ArrayList對象來容納一組point結構,代碼如下: public sealed class Program { public static void Main() { ArrayList a = new ArrayList(); Point p; //分配一個Point(不在堆中分配) for (int i = 0; i < 10; i++) { p.x = p.y = i; //初始化值類型中的成員 a.Add(p); //對值類型裝箱,將引用添加到ArrayList中 } } }   每次迴圈迭代都初始化一個ponit的值類型欄位,並將該point存儲到ArrayList中。但思考一下ArrayList中究竟存儲了什麼?是Point結構,Point結構的地址,還是其他完全不同的東西?要知道正確答案,必須研究ArrayList的Add方法,瞭解他的參數被定義成什麼類型。本例的Add方法原型如下: public virtual int Add(Object value); 可以看出Add獲取的是一個Object參數,也就是說,Add獲取對托管堆上的一個對象的引用來作為參數。但之前的代碼傳遞的是p,也就是一個Point,是值類型。為了使代碼正確工作,Point值類型必須轉成真正的,在堆中托管的對象,而且必須獲取對該對象的引用。   將值類型轉換成引用類型要使用裝箱機制。下麵說說對值類型的實例進行裝箱時發生的事情: 1,在托管堆中分配記憶體。分配的記憶體量是值類型各欄位所需的記憶體量,還要加上托管堆所有對象都有的兩個額外成員(類型對象指針和同步塊索引)所需要的記憶體量 2,值類型的欄位複製到新分配的堆記憶體 3,返回對象地址。現在該對象是對象引用;值類型成了引用類型 C#編譯器檢測到上述代碼是向要求引用類型的方法傳遞值類型,所以自動生成代碼對對象進行裝箱。所以在運行時,當前存在於Point值類型實例p中的欄位複製到新分配的Point對象中。已裝箱Point對象(現在是引用類型)的地址返回並傳給Add方法。Point對象一直存在於堆中,直至被垃圾回收。Point值類型變數p可被重用,因為ArrayList不知道關於他的任何事情。在這種情況下,已裝箱類型的生存期超過了為裝箱值類型的生存期。   拆箱: 假定要用一下代碼獲取ArrayList的第一個元素: Point p=(Point)a[0]; 他獲取ArrayList的元素0包含的引用,視圖將其放到Point值類型的實例p中。為此,已裝箱Point對象中的所有欄位都必須複製到值類型變數中,後者線上程棧上。CLR分兩步完成複製。第一步獲取已裝箱Point對象中的各個Point欄位地址。這個過程稱為拆箱。第二部將欄位包含的值從堆複製到基於棧的值類型實例中   拆箱不是直接將裝箱過程倒過來。拆箱的代碼比裝箱低得多。拆箱其實就是獲取指針的過程,該指針指向包含在一個對象中的原始值類型。其實,指針指向的是已裝箱實例中的未裝箱部分。所以和裝箱不同,茶香不要求在記憶體中複製任何位元組,知道這個重要區別後,還應知道的一個重點是,往往緊接著一次欄位複製。 已裝箱值類型實例在拆箱是,內部發生下麵這些事情: 1,如果包含“對已裝箱值類型的引用”的變數為null,拋出NullReferenceException異常 2,如果引用的對象不是所需值類型的已裝箱實例,拋出InvalidCastException異常   第二條意味著一下代碼的工作方式和你想的可能不一樣: public static void Main() { Int32 x = 5; Object o = x; //對x裝箱,o引用已裝箱對象 Int16 y = (Int16)o; //拋出InvalidCastException異常 } 從邏輯上說,完全能獲取o引用的已裝箱Int32,將其強制轉換為int16.但是對對象進行拆箱時,只能轉型為最初未裝箱的值類型--本例Int32,以下是正確寫法: public static void Main() { Int32 x = 5; Object o = x; //對x裝箱,o引用已裝箱對象 Int16 y = (Int16)(Int32)o; //先拆箱為正確類型,再轉型 } 在看以下代碼: public static void Main() { Point p; p.x = p.y = 1; Object o = p; //對p裝箱,o引用已裝箱實例   //將Point的x欄位變成2 p = (Point)o; //對o拆箱,將欄位從已裝箱的實例複製到棧變數中 p.x = 2; //更改棧變數的狀態 o = p; //將p裝箱,o引用新的已裝箱實例 } 最後三行代碼的唯一目的就是將Point的x欄位從1變成2.為此,首先要執行一次拆箱,在執行一次欄位複製,再更改欄位(棧上),最後執行一次裝箱(在托管堆上創建全新的已裝箱實例)。由此可以看出裝箱拆箱對應用程式性能的影響。 問: public static void Main() { Int32 v = 5; Object o = v; v = 123; Console.WriteLine(v+","+(Int32)o); } 上述代碼發生了多少次裝箱?
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 上一篇遠程桌面採用的獨立服務配置不適用於過多用戶,這一篇採用超級Internet伺服器搭建vnc服務可以解決多用戶問題。 vnc之xinetd服務搭建配置 Linux遠程桌面(一):vnc之獨立服務配置 步驟一: (1)檢查是否安裝vnc服務和xinetd服務 (2)若沒有這兩項服務就需要安裝 /e ...
  • 前提: 公司除了領導和開發人員具有管理員許可權,其他人員使用的都是域賬號。 過程: 應要求開發一個 winfrom項目,使用data.oracleclient 本地開發,調試無誤,放到伺服器共用域用戶使用,報錯 OCIEnvCreate 失敗, 返回代碼為-1 , 百思不得其解,網上也有很多這種錯誤, ...
  • 1. Create a windows service project 2. Add Reference to the assembly which contains the contract and its implementation. 3. Remove the Service1.cs, ad ...
  • 一、Parallel類 Parallel類提供了數據和任務的並行性; 二、Paraller.For() Paraller.For()方法類似於C#的for迴圈語句,也是多次執行一個任務。使用Paraller.For()方法,可以並行運行迭代,迭代的順序沒有定義。 在For()方法中,前兩個參數是固定 ...
  • Hello all , 我又回來了 這次我們真是開始來聊聊開源項目里,小而有用的模塊或者組件的開發思想。 同時,軟體已經更新到1.60的版本了,支持新用戶註冊,可以不再使用統一的test賬戶了。 您可以通過以下路徑進行下載: 1、在GitHub上fellow一下項目,下載到本地,生成一下,即可獲取最 ...
  • 很多人都說使用反射會有性能問題,那到底會比直接調用慢多少呢,下麵就來測試一下。 直接調用vs反射調用 下麵就來寫個demo來驗證下直接調用和反射調用的性能差異,代碼如下: 測試結果: 從100萬次調用結果來看,確實就像很多人所說的,兩者在性能上具有數量級的差距。 為什麼反射有性能損失 既然反射性能有 ...
  • C#編譯器對靜態類進行瞭如下限制: 1,靜態類必須直接從基類System.Obect派生,從其他任何基類派生都沒有意義。繼承只適用於對象,而你不能創建靜態類的實例 2,靜態類不能實現任何介面,這是因為只有適用類的實例時,才可調用介面方法 3,靜態類只能定義靜態成員(欄位,方法,屬性和事件),任何實例 ...
  • 前面總結了反射的使用,這一篇結合一個完整的項目來總結下反射的實際應用。 項目結構 如下圖: 定義插件介面 在項目ConsoleApplication6.IService中,定義了兩個介面,Run代表行駛,Trun代表轉向,如下代碼: 插件程式實現 這裡新建了兩個項目分別實現插件程式,分別是Conso ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...