托管资源,非托管资源,资源释放 IDisposable

能在.Net Framework中找到的类产生的对象,都是 托管资源。自定义的类型需要注意是否用到了非托管资源。

非托管资源 则是CLR无法对这些资源管理,这些资源的申请、释放必须由使用者自行管理。例如,像Win32 API编程中的文件句柄,上下文句柄、窗口、GDI+对象、COM对象、数据库连接或网络连接等(P/Invoke)资源、StreamReader等各种流都属于非托管资源。

资源释放,一般指的是非托管资源释放,托管资源由GC自动管理。

终结器 应用 备注
~ClassName(){} 必须 确保释放非托管资源,GC会隐式调用
Finalize(){} 尽量不用 很像C++的析构函数

在Finalize方法中应该尽量避免引用其他实现了Finalize方法的对象,减少依赖性问题。
一个实现了Finalize方法的对象必需等两次GC才能被完全释放,可见,这种“自动”释放资源的方法并不能满足我们的需要,因为我们不能显示的调用它(只能由GC调用),而且会产生依赖型问题。我们需要更准确的控制资源的释放。
以此代码中最好避免使用终结器,也应尽量少让代码的逻辑使用到终结器。

Dispose是提供给我们显示调用的方法。使用using()调用,能自动调用对象Dispose方法。Dispose一般实现模式如下:
IDisposiable是显示释放对象的接口,实现IDisposiable接口的类,可以显式地释放对象。

/// <summary>
/// 含有非托管资源的类,继承Dispose
/// </summary>
public class DisposePattern : IDisposable
{
    // 是否释放,true已释放
    private bool _isDisposed = false;
    // 锁对象
    private static readonly object _clockObj = new object();
    // 非托管资源
    private System.IO.FileStream fs = new System.IO.FileStream("test.txt", System.IO.FileMode.Create);
    // 托管资源
    private List<string> data = new List<string>();

    // 包含非托管资源的类才有意义,防止使用者没有显示调用Dispose方法
    ~DisposePattern()
    {
        // false时,只处理非托管资源即可,托管资源会自动清理
        Dispose(false);
    }
    // 显示调用
    public void Dispose()
    {
        // true时要同时处理托管资源、非托管资源
        Dispose(true);
        //告诉GC不需要再调用Finalize方法,
        //因为资源已经被显示清理
        GC.SuppressFinalize(this);
    }
    // 实际释放资源的地方
    // 受保护的虚方法,考虑到这个类型被继承时,子类也许会实现自己的Disponse。这时子类要调用base.Disponse。
    protected virtual void Dispose(bool disposing)
    {
        //不需要多次释放
        if (_isDisposed) return;
        //由于Dispose方法可能被多线程调用,所以加锁以确保线程安全
        lock (_clockObj)
        {
            if (disposing)
            {
                //说明对象的Finalize方法并没有被执行,在这里可以安全的引用其他实现了Finalize方法的对象
                //释放托管资源,包括事件监听
                if (data != null)
                {
                    data.Clear();
                    data = null;
                }
            }
            //释放非托管资源
            if (fs != null)
            {
                fs.Dispose();
                fs = null; //标识资源已经清理,避免多次释放
            }
            // 让类型知道自己已经被释放
            _isDisposed = true;
        }
    }

}
/// <summary>
/// 含有非托管资源的派生类,基类实现了Dispose
/// </summary>
public class DerivedClass : DisposePattern
{
    // 是否释放
    private bool _isDisposed = false;
    // 锁对象
    private static readonly object _clockObj = new object();
    protected override void Dispose(bool disposing)
    {
        //不需要多次释放
        if (_isDisposed) return;
        lock (_clockObj)
        {
            try
            {
                //清理自己的资源,实现模式与DisposePattern相同
                if (disposing)
                {
                    //释放托管资源
                }
                //释放非托管资源
            }
            finally
            {
                //基类释放自己的资源
                base.Dispose(disposing);
                _isDisposed = true;
            }
        }
    }

}

当然,如果DerivedClass本身没有什么资源需要清理,那么就不需要重写Dispose方法。

原文地址:https://www.cnblogs.com/wesson2019-blog/p/12767631.html