• 使用using清理非托管资源


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

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

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

         写段常用的连接SQL语句的模型:

    View Code
    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掉,也会由终结器将这块内存回收。但是,就增加了这块垃圾的代数。

      假设说我们写了这样的代码:

    View Code
    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去关闭了。

      这是非常不好的做法。于是,我们可以想到异常处理:

    View Code
     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棒很多的地方,呵呵!

    View Code
    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();

    代码量是不是小了很多呢?优雅了许多呢!

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

      接下来,再来看下我们常用的使用数据库的方式:

    View Code
    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语句来将之代码重构:

    View Code
     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-finally的处理形式:

    View Code
     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吧!

    View Code
     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来进行非托管资源的资源回收。

          
    多思考,多创新,才是正道!
  • 相关阅读:
    省选测试13
    省选测试12
    省选测试11
    省选测试9
    省选测试10
    省选测试8
    省选测试7
    省选测试6
    倍增 LCA && ST表
    博客园markdown
  • 原文地址:https://www.cnblogs.com/shuang121/p/1992890.html
Copyright © 2020-2023  润新知