我们都知道,垃圾回收可以分为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来进行非托管资源的资源回收。