Finalize()的调用将(最终)发生在一次"自然的"垃圾回收或用程序通过GC.Collect()强制回收的过程中,所以这样看来,终结器方法就是让类对象释放内部非托管资源的地方。nice,现在我们可以像这样来编写清理非托管资源的代码:
//数据库上下文类 public class SqlDbContext { //...(其他被引用的对象实例) //类中包含的非托管资源(需要调用 Dispose()函数进行资源的释放) SqlConnection sqlConnection = new SqlConnection("..."); ~SqlDbContext() { //这里清除非托管资源 this.sqlConnection.Dispose(); } }
这样被构建的对象被叫做可终结对象。
有关于终结过程的细节,在《C#与.NET4高级程序设计(第5版)》书中是这样描述的:
从以上的内容我们得知:通过Finalize()来清除非托管资源的时机只能是在.NET对象被垃圾回收的过程中,而且终结过程是一个消耗不小的动作。
问题又来了:很多非托管资源都非常宝贵(如数据库和文件句柄),所以这些资源应该在使用完后尽快地被清除,而不能依靠垃圾回收的发生,那么这些资源应该以怎样的形式被显示地释放呢?
构建可处置对象—非托管资源处理第二式
除了重写 Finalize() 之外,类还可以实现 IDisposable 接口,它定义了一个名为 Dispose() 的方法:
public interface IDisposable { void Dispose(); }
它的使用方法就是:在类的Dispose()方法中编写非托管资源的释放的代码,程序员可以在这个对象不再需要的时候手动调用对象的Dispose()方法来达到及时释放非托管资源的目的。
于是你可以像这样来编写类:
//数据库上下文类 public class SqlDbContext:IDisposable { //...(其他被引用的对象实例) //类中包含的非托管资源(需要调用 Dispose()函数进行资源的释放) SqlConnection sqlConnection = new SqlConnection("..."); public void Dispose() { //这里清除非托管资源 this.sqlConnection.Dispose(); } }
采用这种方式来释放非托管资源的类被称作为可处置对象。
在这里还要补充一点,C#提供了一个语法糖来简化调用Dispose()操作,如下:
SqlDbContext context = new SqlDbContext(); try { //在此作用域内使用SqlDbContext类对象context } finally { //确保使用完后调用Dispose()方法 context.Dispose(); }
上面这段代码等同于下面这段代码:
using (SqlDbContext context = new SqlDbContext()) { //在此作用域内使用SqlDbContext类对象context }
c++程序员说:“你这还不是要自己手动调用,如果我忘记调用 Dispose() 那岂不是一切都玩完!”
c#程序员冷笑一声,“非也,非也,我来传授你最后一招吧!”
非托管资源最强模式 — 双剑合璧
人非圣贤,孰能无过。程序员也会有失手的时候,比如,忘记调用 Dispose() 方法...
这个时候就必须设计一个万无一失的方法,达到一个目的:就是不管有没有手动调用Dispose(),非托管资源最终都应该被妥妥地释放掉。为了解决这个问题,我们可以如下去定义一个可处置对象类:
//数据库上下文类 public class SqlDbContext:IDisposable { //...(其他被引用的对象实例) //类中包含的非托管资源(需要调用 Dispose()函数进行资源的释放) SqlConnection sqlConnection = new SqlConnection("..."); ~SqlDbContext() { //这里清除非托管资源 this.sqlConnection.Dispose(); } public void Dispose() { //这里清除非托管资源 this.sqlConnection.Dispose(); //跳过终结过程 GC.SuppressFinalize(this); }
可以看到,这个类中即有终结方法的重写也有Dispose()方法,这样就能保证:程序员若忘记调用Dispose()方法释放非托管资源,那么对象就会在垃圾回收的过程中调用终结方法来释放非托管资源;若程序员调用了Dispose()方法,那么 GC.SuppressFinalize(this) 会保证在垃圾回收过程中不再会调用对象的终结方法,避免不必要的资源开销。可谓“双剑合璧”,保万无一失。