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

 

      

你可能感兴趣的:(in)