【转】实现IDisposable接口
与引用计数不同,垃圾回收只保证在以后的某个时间终止无用对象。这种终止方式的不确定性会产生什么样的影响呢?如果程序中为对象在其生存期内分配稀有资源会出现什么情况呢?这里提到的“稀有资源”,是指为专属访问而打开的文件、Windows对象的句柄和数据库的连接等。作为一个优秀的编程人员,应当能通过实现对象的析构函数来释放所有需要的资源。但是,垃圾回收器只是周期性地运作,因此垃圾回收器在运行它的析构函数和释放对象时,该对象往往已经有较长时间没有被使用。从这个意义上讲,所有稀有资源也是空闲的。
这样,如果一个对象在其生存期内必须分配稀有资源时,它应当使用一个公开的拆卸方法,使得用户尽可能显式地释放对象。Microsoft推荐使用Dispose方法,实际上,为了帮助体现这种形式,CLR提供了名为IDisposable的接口:
public interface IDisposable
{
void Dispose();
}
甚至如果一个类实现了IDisposable,在许多情况下这是提供析构函数的好办法。如果用户为拆卸对象调用Dispose失败(这种情况总会发生),它就起安全网的作用。下面例子中的ResouceWrapper类演示了该实现模式。
public class ResourceWrapper : IDisposable
{
public void Dispose()
{
//1. Call Dispose on contained disposable objects
//2. Free any contained unmanaged resources
//3. Suppress finalization as shown below:
GC.SuppressFinalize(this);
}
~ResourceWrapper()
{
//1. Free any contained unmanaged resources
//2.Do NOT call Dispose on contained managed objects
}
}
这里详细描述每个Dispose步骤的细节:
(1) 针对所有包含的托管对象调用Dispose。如果对象指派了其他可清除(disposable )对象,那么对每一个调用Dispose。
(2) 释放所有包含的非托管资源。如果对象指派的资源不采用垃圾回收,应当使用适当机制来释放这些资源。
(3) 限制终止操作。Dispose一旦运行,垃圾回收器不再需要终结对象。为了提高垃圾回收的性能,确保对象不会重复释放一个资源,应如上一个例子那样,调用GC.SuppressFinalize方法。这将避免垃圾回收器执行析构函数。
您可能对析构函数中第2步还存在疑虑,为什么不能对包含的托管资源对象调用Dispose呢?问题在于垃圾回收的非确定性。因为不光对垃圾回收时间的不可预测,而且对垃圾回收的顺序也是不可预测的。换句话说,在析构函数运行时,包含的托管对象很有可能已经执行了垃圾回收操作,如果试图对已释放的对象再调用该方法,会引发异常出现。基于这个理由,千万不要在析构函数中调用包含的托管对象,因为此时它们可能已不存在。
1. 使用可清除对象
初一看,使用可清除对象看起来似乎很简单。首先生成该对象,然后使用,使用结束后调用Dispose。例如:
// How NOT to use a disposable object.
public void SomeFunction()
{
ResourceWrapper rw = new ResourceWrapper();
// Use the resource wrapper object.
// What if an exception happened?
// When finished, dispose of the object
rw.Dispose();
}
前面代码的问题是未考虑到异常的影响。若使用对象过程中出现异常,程序跳过对Dispose的调用和所有内部实现的实时资源释放逻辑,直接进入最近的try模块,然后在try模块的finally部分使用Dispose调用。正确使用可清除对象的方式如下例所示:
public void SomeFunction()
{
ResourceWrapper rw = new ResourceWrapper();
try
{
// use the resource wrapper object
}
finally
{
// Now dispose is called,guaranteed.
rw.Dispose();
}
}
2. 使用Using关键字
在finally部分进行Dispose调用,能保证出现异常时仍然能够调用该方法。但是仅是为了一个可清除对象编写异常处理代码又显得较为麻烦,因此C#提供了一种捷径:使用using关键字。下面的例子显示了使用using关键字后重写的代码:
public void SomeFunction()
{
using (ResourceWrapper rw = new ResourceWrapper())
{
// Use the resource wrapper object
} // Dispose is called on resource wrapper object
}
但是,这里C#继承了C++传统的重载关键字的含义。在上下文中,using对于指定命名空间提供了一种快捷方式,同时对一个指定的可清除对象,它限定了一个特殊的作用域。当超出该作用域,无论出现了异常与否,对象的Dispose方式都会被调用。
注意:
在using关键字后面的表达式必须返回一个实现IDisposable接口的对象。
转自:http://blog.163.com/airlly_520/blog/static/6631405520082654858432/