我们都知道,垃圾回收可以分为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-catch-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-catch-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。有一个观点我不知道是否正确,曾经也引起过很激烈的讨论,就是异常处理会不会降低程序的性能。如果会的话,那么异常的嵌套是不是会让程序的性能指数型降低呢?所以有个原则是:尽量避免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来进行非托管资源的资源回收。
文/
飞林沙 出处/博客园
Visual Studio.NET栏目的最新浏览文章:
•
Expression Studio 4 Ultimate MSDN版和中文版破解 •
《Spring.NET框架参考文档》中文版 •
.NET 模板引擎 EFPlatform.CodeGenerator (代码生成器 静态页生成 生成HTML 模板) •
最佳黑色背景的Visual Studio 2008配置-尤其适合开发人员使用(提供下载) •
全国省份,城市,地区全数据(SQL版与XML版)包括各城市邮编 •
脱离.Net Framework运行doNet程序的简单方法 •
《Microsoft Visual C# 2008 Step by Step》PDF+Source Code •
Visual Studio 2008 C# .net 自已动手制作串口调试助手 •
Mastering Microsoft Visual Basic 2010 •
使用.NET3.5的缓存池和SocketAsyncEventArgs类创建socket服务器 •
如何从SharePoint 2007迁移到SharePoint 2010 •
DotNetNuke 5 User’s Guide: Get Your Website Up and Running •
DotnetCharting控件的破解方法 •
.NET 小游戏 (程序+详细文档+源代码) :走迷宫 •
.NET使用DotNetCharting控件生成报表统计图总结 •
.NET中对串口(COM)读写操作方式汇总