使用using清理非托管资源

我们都知道,垃圾回收可以分为DisposeFinalize两类,关于这两者的区别已经太多了,一个是正常的垃圾回收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来进行非托管资源的资源回收。

posted on 2009-10-18 00:34  jef  阅读(314)  评论(0编辑  收藏  举报

导航