我們都知道,垃圾回收可以分為Dispose和Finalize兩類,關於這兩者的區別已經太多了 ,一個是正常的垃圾回收GC所調用的方法,另外一個是終結器Finalizer,所調用的方法,在 Effective C#一書中,有著明確的建議是說使用IDispose接口來代替Finalize。原因是因為 Finalize終結會增加垃圾回收對象的代數,從而影響垃圾回收。
有了上述的原因,我們現在只來看使用IDispose接口的類。
在.NET中,絕大多數的類都是運行在托管的環境下,所以都由GC來負責回收,那麼我們就 不需要實現IDispose接口,而是由GC來自動負責。可是有一些類使用的是非托管資源,那麼 這個時候,我們就應該去實現IDispose接口,說個比較常用的SqlConnection之類。
寫段常用的連接SQL語句的模型:
string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings ["Study1ConnectionString1"].ConnectionString;
SqlConnection thisConnection = new SqlConnection(connectionString);
thisConnection.Open();
SqlCommand thisCommand = new SqlCommand();
thisCommand.Connection = thisConnection;
thisCommand.CommandText = "select * from [User]";
thisCommand.ExecuteNonQuery();
thisConnection.Close();
其實,作為非托管資源,為了防止我們忘記調用Close,一般都實現了Finalize,因此, 即使我們沒有Close掉,也會由終結器將這塊內存回收。但是,就增加了這塊垃圾的代數。
假設說我們寫了這樣的代碼:
string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings ["Study1ConnectionString1"].ConnectionString;
SqlConnection thisConnection = new SqlConnection(connectionString);
thisConnection.Open();
SqlCommand thisCommand = new SqlCommand();
thisCommand.Connection = thisConnection;
thisCommand.CommandText = "select * form [User]"; //SQL語句錯誤
thisCommand.ExecuteNonQuery();
thisConnection.Close();
這樣的話,我們打開的SqlConnection就沒有關閉,只能等待Finalize去關閉了。
這是非常不好的做法。於是,我們可以想到異常處理:
SqlConnection thisConnection = null;
try
{
string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings ["Study1ConnectionString1"].ConnectionString;
thisConnection = new SqlConnection(connectionString);
thisConnection.Open();
SqlCommand thisCommand = new SqlCommand();
thisCommand.Connection = thisConnection;
thisCommand.CommandText = "select * form [User]";
thisCommand.ExecuteNonQuery();
}
finally
{
if (thisConnection != null)
{
thisConnection.Close();
}
}
這樣做就不錯了,但是代碼看起來有些丑陋,可是使用using就讓代碼優雅了很多,這也 是C#比JAVA棒很多的地方,呵呵!
string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings ["Study1ConnectionString1"].ConnectionString;
using (SqlConnection thisConnection = new SqlConnection())
{
thisConnection.Open();
SqlCommand thisCommand = new SqlCommand();
thisCommand.Connection = thisConnection;
thisCommand.CommandText = "select * form [User]";
thisCommand.ExecuteNonQuery();
}
代碼量是不是小了很多呢?優雅了許多呢!
其實,在IL的位置,代碼仍然是一樣的,他同樣把代碼給編譯成了try-finally的處理形 式!
接下來,再來看下我們常用的使用數據庫的方式:
string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings ["Study1ConnectionString1"].ConnectionString;
SqlConnection thisConnection = new SqlConnection(connectionString);
thisConnection.Open();
SqlCommand thisCommand = new SqlCommand();
thisCommand.Connection = thisConnection;
thisCommand.CommandText = "select * from [User]";
SqlDataReader thisReader = thisCommand.ExecuteReader();
thisReader.Close();
thisConnection.Close();
還是上面的問題,我們考慮用using語句來將之代碼重構:
string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings ["Study1ConnectionString1"].ConnectionString;
using (SqlConnection thisConnection = new SqlConnection (connectionString))
{
thisConnection.Open();
SqlCommand thisCommand = new SqlCommand();
thisCommand.Connection = thisConnection;
thisCommand.CommandText = "select * from [User]";
using (SqlDataReader reader = thisCommand.ExecuteReader())
{
while (reader.Read())
{
//操作
}
}
}
我先把這段代碼翻譯成我們熟悉的try-finally的處理形式:
SqlConnection thisConnection = null;
try
{
string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings ["Study1ConnectionString1"].ConnectionString;
thisConnection = new SqlConnection(connectionString);
thisConnection.Open();
SqlCommand thisCommand = new SqlCommand();
thisCommand.Connection = thisConnection;
thisCommand.CommandText = "select * from [User]";
SqlDataReader reader = null;
try
{
reader = thisCommand.ExecuteReader();
while (reader.Read())
{
//操作
}
}
finally
{
reader.Close();
}
}
finally
{
thisConnection.Close();
}
更丑陋的代碼吧!所以有個原則是:盡量避免using語句的嵌套。
怎麼樣解決呢?很容易,自己寫我們的try-finally吧!
SqlConnection thisConnection = null;
SqlDataReader reader = null;
try
{
string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings ["Study1ConnectionString1"].ConnectionString;
thisConnection = new SqlConnection(connectionString);
thisConnection.Open();
SqlCommand thisCommand = new SqlCommand();
thisCommand.Connection = thisConnection;
thisCommand.CommandText = "select * from [User]";
reader = thisCommand.ExecuteReader();
while (reader.Read())
{
//操作
}
}
finally
{
if (thisConnection != null)
{
thisConnection.Close();
}
if (reader != null)
{
reader.Close();
}
}
這樣就好了!
關於using 的這節我就寫到這,最後對全文做個總結,其實就是一句話:盡量使用using 來進行非托管資源的資源回收。