作用
此接口的主要用途是释放非托管资源。 当不再使用托管对象时,垃圾回收器会自动释放分配给该对象的内存。 但无法预测进行垃圾回收的时间。 另外,垃圾回收器对窗口句柄或打开的文件和流等非托管资源一无所知。
检查
在 Visual Studio 中运行代码分析的时候,如果某一个类中,包含了实现 IDisposable 模式的属性,则会提醒你该类同样需要实现 IDisposable 模式,而实现 IDisposable 模式是有一套固定的模式的。这种模式能更有效的防止内存泄漏,也能在某些地方更简洁的书写代码,比如 using 关键字就是专为此而生。
using 关键字
提供能确保正确使用 IDisposable 对象的方便语法。以下是示例:
using (Font font = new Font("Arial", 10.0f)) {
byte charset = font.GdiCharSet;
}
using 关键字保证在离开 using 作用域的时候一定会调用 font.Dispose
。所以,如果只是在局部使用了 IDisposable 变量,最好这样包起来,以防止内存泄漏。
实现 IDisposable
MSDN 提供了一套标准的实现模板,地址在这:CA1063: Implement IDisposable correctly,我稍微有一点修改的实现了自己的:
/// <summary>
/// @author Easily
/// </summary>
public class BaseObject : IDisposable {
private bool _enabled = true;
public bool enabled {
get { return _enabled; }
}
public void Dispose() {
if (_enabled) {
Dispose(true);
}
}
private void Dispose(bool value) {
if (_enabled) {
_enabled = false;
Destroy();
if (value) {
GC.SuppressFinalize(this);
}
}
}
virtual protected void Destroy() {
// release managed resource here
}
~BaseObject() {
Dispose(false);
}
}
需要释放的资源会在 Destroy
里面得到释放,所以,子类如果有需要释放的资源,必须重写 Destroy
。Destroy
只会被调用一次,之后 enabled
属性会被设置为 false
,如果子类在运行过程中正在调用异步的方法,回调必须检查 enabled
属性。
并发版本实现
以上的实现在并发环境下很容易出问题,并发的情况下,需要对 _enabled 锁定,使用 lock
关键字即可:
lock (_lockObj) {
_enabled = false;
}
所以,这就需要所有用到 _enabled
的地方都进行锁定,还有一个方法,使用 Interlocked
提供的原子操作,以下是并发版本:
/// <summary>
/// @author Easily
/// </summary>
public class BaseObject : IDisposable {
private int _disposed;
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing) {
if (Interlocked.CompareExchange(ref _disposed, 1, 0) != 0) {
return;
}
if (disposing) {
Destroy();
}
}
virtual protected void Destroy() {
//
}
~BaseObject() {
Dispose(false);
}
}