每一個using語句 生成了一個新的嵌套的try/finally塊。我發現這是很糟糕的結構,所以,如果 是遇到多個實現了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();
}
}
(譯注:作 者裡的判斷對象是否為null是很重要的,特別是一些封裝了COM的對象,有些時 候的釋放是隱式的,當你再釋放一些空對象時會出現異常。例如:同一個COM被 兩個不同接口的變量引用時,在其中一個上調用了Dispose後,另一個的調用就 會失敗。在.Net裡也要注意這樣的問題,所以要判斷對象是否為null)
然 而,請不要自作聰明試圖用as來寫這樣的using語句:
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 ();
}
}
這看上去很清爽,但有一個狡猾的 (subtle )的bug。 如果SqlCommand()的構造函數拋出異常,那麼SqlConnection 對象就不可能被處理了。你必須確保每一個實現了IDispose接口的對象分配在在 using范圍內,或者在try/finally塊內。否則會出現資源洩漏。
目前為 止,你已經學會了兩種最常見的情況。無論何時在一個方法內處理一個對象時, 使用using語句是最好的方法來確保申請的資源在各種情況下都得到釋放。當你 在一個方法裡分配了多個(實現了IDisposable接口的)對象時,創建多個using塊 或者使用你自己的try/finally塊。
對可處理對象的理解有一點點細微的 區別。有一些對象同時支持Disponse和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方法會釋放更多的資源,它還會告 訴GC,這個對象已經不再須要析構了(譯注:關於C#裡的析構,可以參考其它方 面的書籍)。Dispose會調用GC.SuppressFinalize(),但Close()一般不會。結果 就是,對象會到析構隊列中排隊,即使析構並不是須要的。當你有選擇時, Dispose()比Colse()要好。你會在原則18裡學習更更精彩的內容。
Dispose()並不會從內存裡把對象移走,對於讓對象釋放非托管資源來說 是一個hook。這就是說你可能遇到這樣的難題,就是釋放一個還在使用的對象。 不要釋放一個在程序其它地方還在引用的對象。
在某些情況下,C#裡的 資源管理比C++還要困難。你不能指望確定的析構函數來清理你所使用的所有資 源。但垃圾回收器卻讓你更輕松,你的大從數類型不必實現IDisposable接口。 在.Net框架裡的1500多個類中,只有不到100個類實現了IDisposable接口。當你 使用一個實現了IDisposeable接口的對象時,記得在所有的類裡都要處理它們。 你應該把它們包含在using語句中,或者try/finally塊中。不管用哪一種,請確 保每時每刻對象都得到了正確的釋放。
返回教程目錄