《Effective C#》讀書筆記——條目15:使用using和try/finally清理資源

來源:互聯網
上載者:User

  在.NET環境中,非託管系統資源由開發人員來負責釋放,且非託管系統資源必須顯式的使用IDisposable介面的Dispose()來釋放(詳見:瞭解.NET記憶體管理機制)。所有封裝或使用了非託管資源的類型都實現了IDisposable介面。同時這些類也在終結器中調用Dispose(),保證了開發人員在忘記資源釋放的時候仍然能夠正常的釋放掉資源(對象資源會一直停留在記憶體中,直到終結器被調用),這會導致資源在記憶體中停留的時間更長,導致應用程式會佔用更多的系統資源。

 

閱讀目錄:

    1.使用using關鍵字

        1.1 安全銷毀對象

    2.同時銷毀多個可銷毀對象

    3.釋放可銷毀對象的方式

    小節

    參考&進一步閱讀

 

1.使用 using 關鍵字

   在C#中為我們添加了一個現實釋放非託管資源的關鍵字:using。using語句其實是一個C#語言的文法糖,當我們在using語句中分配可釋放對象時,C#編譯器將會自動在每個對象外產生一個try/finally塊來包裹住分配的對象,保證資源的及時釋放,即使拋出了異常也一樣。如果要使用一個可銷毀的對象,使用using語句能夠以最簡單的方式保證你的對象可以正常銷毀。看下面使用了using語句的代碼:

1             SqlConnection myConnection = null;2 3             using (myConnection = new SqlConnection(connectionString))4             {5                 myConnection.Open();6             }

 

這段代碼和下面的代碼產生的IL代碼完全一致

1             try2             {3                 myConnection = new SqlConnection(connectionString);4                 myConnection.Open();5             }6             finally7             {8                 myConnection.Dispose();9             }

 

使用using語句的注意事項:

如果using語句中分配的變數的類型沒有實現IDisposable介面,編譯器將會拋出異常。

 

1.1 安全銷毀對象

   對於一些可能實現或未實現IDisposable介面的對象,或者無法確定是否應該用using語句包裹某個對象時,由於其不確定性我們可以使用as操作符進行安全的銷毀,如下:

1             object obj = Factory.CreateResource();2             //如果不確定obj是否實現了IDisposable介面,下面是安全的銷毀方式3             using (obj as IDisposable)4             {5                 Console.WriteLine(obj.ToString());6             }

如果obj實現了IDisposable介面,那麼using語句將產生清理代碼;反之,using語句將變成using(null) ,這並不會拋出異常,也不會有任何其他意料之外的操作。

 

2.同時銷毀多個可銷毀對象

   通過前面我們知道使用using語句時編譯器會將其轉換為try/finally語句,這能夠保證我們可以正確的將非託管對象銷毀。但是當我們想要同時銷毀多個對象時情況會有一點細微的變化,看下面的代碼:

 1         public void ExecuteCommand(string connString, string commandString) 2         { 3             using (SqlConnection myConnection = new SqlConnection(connString)) 4             { 5                 using (SqlCommand mySqlCommand = new SqlCommand(commandString, myConnection)) 6                 { 7                     myConnection.Open(); 8                     mySqlCommand.ExecuteNonQuery(); 9                 }10             }11         }

 

 我們看到方法內部有兩個非託管對象需要被釋放,上面的代碼啟動並執行效果和我們的預期並沒有差別,我們通過查看IL代碼發現,這個樣本產生的IL和下面的樣本是一樣的:

View Code

 1         public void ExecuteCommand(string connString, string commandString) 2         { 3             SqlConnection myConnection = null; 4             SqlCommand mySqlCommand = null; 5             try 6             { 7                 myConnection = new SqlConnection(connString); 8                 try 9                 {10                     mySqlCommand = new SqlCommand(commandString, myConnection);11 12                     myConnection.Open();13                     mySqlCommand.ExecuteNonQuery();14                 }15                 finally16                 {17                     if (mySqlCommand != null)18                         mySqlCommand.Dispose();19                 }20             }21             finally22             {23                 if (myConnection != null)24                     myConnection.Dispose();25             }26         }

 

 上面的代碼並沒有上面問題,運行效果可以達到了我們的預期,不過我們可以做得好點,直接使用try/finally語句,避免在這樣情況中使用using語句而產生了多餘的try/finally語句,這樣寫會更好:

 1 public void ExecuteCommand(string connString, string commandString) 2         { 3             SqlConnection myConnection = null; 4             SqlCommand mySqlCommand = null; 5             try 6             { 7                 myConnection = new SqlConnection(connString); 8                 mySqlCommand = new SqlCommand(commandString, myConnection); 9 10                 myConnection.Open();11                 mySqlCommand.ExecuteNonQuery();12             }13             finally14             {15                 if (mySqlCommand != null)16                     mySqlCommand.Dispose();17                 if (myConnection != null)18                     myConnection.Dispose();19             }

 

 注意:

必須保證每個實現了IDisposable介面的對象都放在了using或try/finally中,否則就可能會發生資源流失。

 

3.釋放可銷毀對象的方式

   我們發現在我們釋放可銷毀對象時。有的類型不但提供Dispose()方法還提供了一個Close方法,比如前面樣本中SqlConnection類的myConnection對象,我們可以知道調用myConnection對象的Close方法關閉資料庫連接,但是這和調用它的Dispose()有一些差別:Dispose()方法將調用GC.SuppressFinalize()方法,而Close()方法一般則不會,因此,即使已經不需要終結,但對象仍舊在終結隊列中。如果兩種方式你可以選擇,應該優先使用Dispose方法。

  同時我們需要知道:Dispose()並不是將對象從記憶體中移除,而只是讓對象釋放掉其中的非託管資源

 

小節

   .Net Framework 中只有不到100個類實現了IDisposable介面,當我們使用實現了IDisposable介面的類時,我們要保證能夠正確的進行清理工作,可以將這些對使用using語句或者是try/finally語句包裹起來。無論使用哪種方式,都要保證對象在任何時候、任何情況下都被正確的銷毀。

 

參考資源&進一步閱讀

終結器

using 語句

實現 Finalize 和 Dispose 以清理非託管資源

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.