赞助商
我们都知道,垃圾回收可以分为Dispose和Finalize两类,关于这两者的区别已经太多了,一个是正常的垃圾回收GC所调用的方法,另外一个是终结器Finalizer,所调用的方法,在Effective C#一书中,有着明确的建议是说使用IDispose接口来代替Finalize。原因是因为Finalize终结会增加垃圾回收对象的代数,从而影响垃圾回收。

有了上述的原因,我们现在只来看使用IDispose接口的类。

在.NET中,绝大多数的类都是运行在托管的环境下,所以都由GC来负责回收,那么我们就不需要实现IDispose接口,而是由GC来自动负责。可是有一些类使用的是非托管资源,那么这个时候,我们就应该去实现IDispose接口,说个比较常用的SqlConnection之类。

写段常用的连接SQL语句的模型:
  1. string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["Study1ConnectionString1"].ConnectionString;
  2. SqlConnection thisConnection = new SqlConnection(connectionString);
  3. thisConnection.Open();
  4. SqlCommand thisCommand = new SqlCommand();
  5. thisCommand.Connection = thisConnection;
  6. thisCommand.CommandText = "select * from [User]";
  7. thisCommand.ExecuteNonQuery();
  8. thisConnection.Close();
复制代码
其实,作为非托管资源,为了防止我们忘记调用Close,一般都实现了Finalize,因此,即使我们没有Close掉,也会由终结器将这块内存回收。但是,就增加了这块垃圾的代数。

假设说我们写了这样的代码:
  1. string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["Study1ConnectionString1"].ConnectionString;
  2. SqlConnection thisConnection = new SqlConnection(connectionString);
  3. thisConnection.Open();
  4. SqlCommand thisCommand = new SqlCommand();
  5. thisCommand.Connection = thisConnection;
  6. thisCommand.CommandText = "select * form [User]";    //SQL语句错误
  7. thisCommand.ExecuteNonQuery();
  8. thisConnection.Close();
复制代码
这样的话,我们打开的SqlConnection就没有关闭,只能等待Finalize去关闭了。

这是非常不好的做法。于是,我们可以想到异常处理:
  1. SqlConnection thisConnection = null;
  2. try
  3. {
  4.     string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["Study1ConnectionString1"].ConnectionString;
  5.     thisConnection = new SqlConnection(connectionString);
  6.     thisConnection.Open();
  7.     SqlCommand thisCommand = new SqlCommand();
  8.     thisCommand.Connection = thisConnection;
  9.     thisCommand.CommandText = "select * form [User]";
  10.     thisCommand.ExecuteNonQuery();
  11. }
  12. finally
  13. {
  14.     if (thisConnection != null)
  15.     {
  16.         thisConnection.Close();
  17.     }
  18. }
复制代码
这样做就不错了,但是代码看起来有些丑陋,可是使用using就让代码优雅了很多,这也是C#比JAVA棒很多的地方,呵呵!
  1. string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["Study1ConnectionString1"].ConnectionString;
  2. using (SqlConnection thisConnection = new SqlConnection())
  3. {
  4.     thisConnection.Open();
  5.     SqlCommand thisCommand = new SqlCommand();
  6.     thisCommand.Connection = thisConnection;
  7.     thisCommand.CommandText = "select * form [User]";
  8.     thisCommand.ExecuteNonQuery();
  9. }
复制代码
代码量是不是小了很多呢?优雅了许多呢!

其实,在IL的位置,代码仍然是一样的,他同样把代码给编译成了try-catch-finally的异常处理形式!

接下来,再来看下我们常用的使用数据库的方式:
  1. string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["Study1ConnectionString1"].ConnectionString;
  2. SqlConnection thisConnection = new SqlConnection(connectionString);
  3. thisConnection.Open();
  4. SqlCommand thisCommand = new SqlCommand();
  5. thisCommand.Connection = thisConnection;
  6. thisCommand.CommandText = "select * from [User]";
  7. SqlDataReader thisReader = thisCommand.ExecuteReader();
  8. thisReader.Close();
  9. thisConnection.Close();
复制代码
还是上面的问题,我们考虑用using语句来将之代码重构:
  1. string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["Study1ConnectionString1"].ConnectionString;
  2. using (SqlConnection thisConnection = new SqlConnection(connectionString))
  3. {
  4.     thisConnection.Open();
  5.     SqlCommand thisCommand = new SqlCommand();
  6.     thisCommand.Connection = thisConnection;
  7.     thisCommand.CommandText = "select * from [User]";
  8.     using (SqlDataReader reader = thisCommand.ExecuteReader())
  9.     {
  10.         while (reader.Read())
  11.         {
  12.             //操作
  13.         }
  14.     }
  15. }
复制代码
我先把这段代码翻译成我们熟悉的try-catch-finally的异常处理形式:
  1. SqlConnection thisConnection = null;
  2. try
  3. {
  4.     string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["Study1ConnectionString1"].ConnectionString;
  5.     thisConnection = new SqlConnection(connectionString);
  6.     thisConnection.Open();
  7.     SqlCommand thisCommand = new SqlCommand();
  8.     thisCommand.Connection = thisConnection;
  9.     thisCommand.CommandText = "select * from [User]";
  10.     SqlDataReader reader = null;
  11.     try
  12.     {
  13.         reader = thisCommand.ExecuteReader();
  14.         while (reader.Read())
  15.         {
  16.             //操作
  17.         }
  18.     }
  19.     finally
  20.     {
  21.         reader.Close();
  22.     }
  23. }
  24. finally
  25. {
  26.     thisConnection.Close();
  27. }
复制代码
更丑陋的代码吧!当我们增加一个using的时候,就增加了一个try-finally。有一个观点我不知道是否正确,曾经也引起过很激烈的讨论,就是异常处理会不会降低程序的性能。如果会的话,那么异常的嵌套是不是会让程序的性能指数型降低呢?所以有个原则是:尽量避免using语句的嵌套。

怎么样解决呢?很容易,自己写我们的try-finally吧!
  1. SqlConnection thisConnection = null;
  2. SqlDataReader reader = null;
  3. try
  4. {
  5.     string connectionString = System.Configuration.ConfigurationManager.ConnectionStrings["Study1ConnectionString1"].ConnectionString;
  6.     thisConnection = new SqlConnection(connectionString);
  7.     thisConnection.Open();
  8.     SqlCommand thisCommand = new SqlCommand();
  9.     thisCommand.Connection = thisConnection;
  10.     thisCommand.CommandText = "select * from [User]";
  11.     reader = thisCommand.ExecuteReader();
  12.     while (reader.Read())
  13.     {
  14.         //操作
  15.     }
  16. }
  17. finally
  18. {
  19.     if (thisConnection != null)
  20.     {
  21.         thisConnection.Close();
  22.     }
  23.     if (reader != null)
  24.     {
  25.         reader.Close();
  26.     }
  27.    
  28. }
复制代码
这样就好了!

关于using 的这节我就写到这,最后对全文做个总结,其实就是一句话:尽量使用using来进行非托管资源的资源回收。


文/飞林沙  出处/博客园
赞助商
赞助商
TOP