<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                # 原則15:使用 using 和 try/finally 清理資源 **By D.S.Qiu** **尊重他人的勞動,支持原創,轉載請注明出處:[http://dsqiu.iteye.com](http://dsqiu.iteye.com)** 非托管資源類型必須使用 IDisposable 接口的 Dispose() 方法釋放。.NET 的這個規則使得釋放資源的職責是類型的使用者,而不是類型和系統。因此,任何時候你使用的類型有 Dispose() 方法,你就有責任調用 Dispose() 釋放資源。最好的方法來保證 Dispose() 被調用時使用 using 語句或 try/finally 塊。 所有包含非托管資源的類型都應該實現 IDisposable 接口。此外,如果你沒有恰當的回收這些類型,它們會被動創建析構函數。如果你忘記回收這些對象,這些非內存資源會在晚些時候析構函數被執行的時候釋放。這就會使得這些對象在內存待的時間更久,從而會使得應用程序因占用系統資源過多而變慢。 幸運的是, C# 語言設計者知道顯示釋放資源是一個很常見的操作。他們給語言添加了關鍵字使得會更容易。 ``` public void ExecuteCommand(string connString, string commandString) { SqlConnection myConnection = new SqlConnection(connString); SqlCommand mySqlCommand = new SqlCommand(commandString, myConnection); myConnection.Open(); mySqlCommand.ExecuteNonQuery(); } ``` 在這個例子中,有兩個可回收的對象沒有恰當地被清理:SqlConnection 和 SqlCommand 。這兩個對象會一直停留在內存中直到它們的析構函數被調用。(這兩個類都從 System.ComponentModel.Component 繼承析構函數。 通過在執行命令和連接結束的時候調用 Dispose 修復這個這個問題: ``` public void ExecuteCommand(string connString, string commandString) { SqlConnection myConnection = new SqlConnection(connString); SqlCommand mySqlCommand = new SqlCommand(commandString, myConnection); myConnection.Open(); mySqlCommand.ExecuteNonQuery(); mySqlCommand.Dispose(); myConnection.Dispose(); } ``` 那樣處理就很好了,除非這個 SQL 命令執行拋出了異常。這時,上面例子的 Dispose() 就不會被執行。使用 using 語句可以保證 Dispose() 被調用。你使用 using 語句分配對象,C# 編譯器就會產生 try/finally 塊包含這些對象: ``` public void ExecuteCommand(string connString, string commandString) { using (SqlConnection myConnection = new SqlConnection(connString)) { using (SqlCommand mySqlCommand = new SqlCommand(commandString, myConnection)) { myConnection.Open(); mySqlCommand.ExecuteNonQuery(); } } } ``` 當你在函數使用一個可回收對象時, using 塊是最簡單方法確保對象被恰當回收。using 語句會產生 try/finally 塊包裹被分配的對象。下面兩段代碼的 IL 代碼是一樣的: ``` SqlConnection myConnection = null; // Example Using clause: using (myConnection = new SqlConnection(connString)) { myConnection.Open(); } // example Try / Catch block: try { myConnection = new SqlConnection(connString); myConnection.Open(); } finally { myConnection.Dispose(); } ``` 如果你對沒有實現 IDisposable 類型上使用 using 語句,C# 編譯器會報錯。例如: ``` // Does not compile: // String is sealed, and does not support IDisposable. using (string msg = "This is a message") Console.WriteLine(msg); ``` using 語句對在編譯時期類型實現 IDisposable 接口才能正常工作。你不能對任意對象使用: ``` // Does not compile. // Object does not support IDisposable. using (object obj = Factory.CreateResource()) Console.WriteLine(obj.ToString()); ``` 快速保護方法是使用 as 語句可以轉換為安全可回收對象不管是否實現 IDisposable 接口: ``` // The correct fix. // Object may or may not support IDisposable. object obj = Factory.CreateResource(); using (obj as IDisposable) Console.WriteLine(obj.ToString()); ``` 如果 obj 實現了 IDisposable ,using 語句會產生清理的代碼。否則,using 語句變為 using(null) 這樣是安全的而且不做任何處理。如果你還是不確定使用 using 塊包裹對象是否是正確的,為了安全:假設那樣做是正確的并且像前面的方法一樣使用 using 包裹對象。 這是只是介紹一個簡單的情況:當你在對象內使用可回收的局部對象,使用 using 語句包裹這個對象。現在看幾個復雜的用法。在第一個例子兩個不同對象需要回收:連接和命令。前面的做法是使用兩個 using 語句,將需要回收的兩個對象分別放在里面。每個 using 語句都會產生一個 try/finally 塊。等價于你寫了下面的代碼: ``` public void ExecuteCommand(string connString, string commandString) { SqlConnection myConnection = null; SqlCommand mySqlCommand = null; try { myConnection = new SqlConnection(connString); try { mySqlCommand = new SqlCommand(commandString, myConnection); myConnection.Open(); mySqlCommand.ExecuteNonQuery(); } finally { if (mySqlCommand != null) mySqlCommand.Dispose(); } } finally { if (myConnection != null) myConnection.Dispose(); } } ``` 每個 using 語句都會創建一個嵌套的 try/finally 塊。幸好,我們很少會在一個分配兩個實現 IDisposable 不同的對象。這種情況下,可以允許那樣,因為它能正常工作。但是,如果你要分配多個實現 IDisposable 的對象時,會是一個很糟糕的實現,我更喜歡自己寫 try/finally 塊: ``` public void ExecuteCommand(string connString, string commandString) { SqlConnection myConnection = null; SqlCommand mySqlCommand = null; try { myConnection = new SqlConnection(connString); mySqlCommand = new SqlCommand(commandString, myConnection); myConnection.Open(); mySqlCommand.ExecuteNonQuery(); } finally { if (mySqlCommand != null) mySqlCommand.Dispose(); if (myConnection != null) myConnection.Dispose(); } } ``` 唯一可以拋棄這種寫法的理由是你能很容易的使用 using 和 as 語句實現: 這看上去很清晰,但是會有一個很狡猾的問題。 如果 SqlCommand() 構造拋出異常 SqlConnection 對象將不會被回收。 myConnection 已經被創建,但是當 SqlCommand 構造函數執行時代碼就不會進入 using 塊。在 using 塊沒有構造好, Dispose 的調用就會被跳過。你必須確保任何實現 IDisposable 的對象的分配要在 using 塊或 try 塊內。否則,資源泄露就會發生。 ``` public void ExecuteCommand(string connString, string commandString) { // Bad idea. Potential resource leak lurks! SqlConnection myConnection = new SqlConnection(connString); SqlCommand mySqlCommand = new SqlCommand(commandString,myConnection); using (myConnection as IDisposable) using (mySqlCommand as IDisposable) { myConnection.Open(); mySqlCommand.ExecuteNonQuery(); } } ``` 到目前為止,你已經學會了兩種最常見的情況了。當你在一個函數只分配一個可回收對象,使用 using 語句是最好方法確保資源被釋放。如果在一個方法中要分配多個對象,創建多個 using 塊或自己寫一個 try/finally 塊。 釋放不同的可回收對象會有一些細微的差別。有些類型同時支持 Dispose 方法和 Close 方法去釋放資源。 SqlConnection 就是其中之一。你像下面那樣可以關閉 SqlConnection : ``` public void ExecuteCommand(string connString, string commandString) { SqlConnection myConnection = null; try { myConnection = new SqlConnection(connString); SqlCommand mySqlCommand = new SqlCommand (commandString, myConnection); myConnection.Open(); mySqlCommand.ExecuteNonQuery(); } finally { if (myConnection != null) myConnection.Close(); } } ``` 這個版本是關閉連接,但跟回收它還是有一些不一樣。Dispose 方法不只是釋放資源:它還會告訴垃圾回收器這個對象不用執行析構函數。 Dispose 會調用 GC.SuppressFinalize() 。 Close 就沒有這樣的操作。結果,對象還待在析構隊列中,即使析構已經不需要。如果你可以選擇, Dispose 會比 Close 更好。你可以查看原則18了解所有細節。 Dispose() 不會將對象從內存中移除。它會觸發對象釋放非托管資源。這意味著你使用已經回收的對象會有問題。上面 SQLConnection 就是例子。 SQLConnection 的 Dispose() 方法斷開了數據庫的連接。在你回收這個連接之后,SQLConnection 還留著內存中,但是不再和數據庫保持連接。所以,在任何地方都不要回收還在被引用的對象。 在某些方面,C# 的資源管理會比 C++ 更困難。你不能依賴最終的析構函數清理所有使用到的資源。但是垃圾回收機制對你再簡單不過了。你使用到絕大多數類都沒有實現 IDisposable 。在 .Net 框架的1500多個類,只有少于100個類實現了 IDisposable 。當你使用到其中的類,記得回收它們。你可以將這些對象放在 using 塊或 try/finally 塊中。無論你使用哪一種方式,確保每次所有對象總是都被恰當釋放。 小結: D.S.Qiu 一直都不知道 Dispose 的作用,并且也很疑惑 Close 和 Dispose 的區別(文末給予了解答,痛快)。但是對 Dispose 還是沒有吃透,C# 執行 Dispose 的內部流程是怎么樣的,都做了哪些操作? 歡迎各種不爽,各種噴,寫這個純屬個人愛好,秉持”分享“之德! 有關本書的其他章節翻譯請[點擊查看](/category/297763),轉載請注明出處,尊重原創! 如果您對D.S.Qiu有任何建議或意見可以在文章后面評論,或者發郵件(gd.s.qiu@gmail.com)交流,您的鼓勵和支持是我前進的動力,希望能有更多更好的分享。 轉載請在**文首**注明出處:[http://dsqiu.iteye.com/blog/2078084](/blog/2078084) 更多精彩請關注D.S.Qiu的博客和微博(ID:靜水逐風)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看