唐元志的技术博客 软件设计与开发

实现Dispose Pattern

2022-01-15
Michael
阅读:

实现Dispose方法其实主要是用来释放非托管资源(unmanaged resource)。而一个类型中如果有嵌套有另一个实现IDisposable接口的实例成员时,通常会级联调用Dispose方法。当然一个类中如果只使用了托管资源,也是可以实现Dispose方法去尽早释放资源从而释放已经分配的内存,比如清空一个List,Dictionary等。

针对非托管资源,GC并不负责释放,那么有两个途径可以释放它们:

  • 主动调用Dispose去释放非托管资源
  • 终结器中调用Dispose方法,类似于C++的析构函数(也不是完全相同,因为析构函数是人为调用,而终结器取决于GC)

类中没有非托管资源

针对类中没有非托管资源,那么类中不需要定义终结器。下面是推荐的模板代码: 类中定义私有变量记录是否已经disposed,调用Dispose()方法去释放资源后,将_disposed标记为true。 如果此时从他处再次调用Dispose(),因为_disposed已经为true,在Dispose(bool)方法中将直接返回。

class BaseClassWithoutFinalizer : IDisposable
{
	// To detect redundant calls
	private bool _disposed = false;

	// Public implementation of Dispose pattern callable by consumers.
	public void Dispose()
	{
		Dispose(true);
	}

	// Protected implementation of Dispose pattern.
	protected virtual void Dispose(bool disposing)
	{
		if (_disposed)
		{
			return;
		}

		if (disposing)
		{
			// TODO: dispose managed state (managed objects).
		}

		_disposed = true;
	}
}

释放非托管资源

例子中假定使用了非托管资源ptr, 下面是推荐的dispose模板代码: 首先实现IDisposable接口,同时定义一个protected virtual Dispose(bool)方法和~BaseClassWithFinalizer() 终结器。

Dispose方法中调用Dispose(true), 这里的true表示确定性释放资源,也就是可以释放托管和非托管资源。资源释放后,使垃圾回收器不再需要调用对象的 Object.Finalize 方法。 因此,调用 SuppressFinalize 方法去阻止垃圾回收器运行终结器。 如果类型没有终结器,则对 GC.SuppressFinalize 的调用不起作用。

在终结器中,调用Dispose(false)去释放非托管资源。这里主要是为了当人为忘记调用Dispose方法后,程序依然可以安全的释放非托管资源。

class BaseClassWithFinalizer : IDisposable
{
	// To detect redundant calls
	private bool _disposed = false;
	private IntPtr ptr = Marshal.AllocHGlobal(2);

	~BaseClassWithFinalizer() => Dispose(false);

	// Public implementation of Dispose pattern callable by consumers.
	public void Dispose()
	{
		Dispose(true);
		GC.SuppressFinalize(this);
	}

	// Protected implementation of Dispose pattern.
	protected virtual void Dispose(bool disposing)
	{
		if (_disposed)
		{
		return;
		}

		if (disposing)
		{
		// TODO: dispose managed state (managed objects).
		}

		// TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
		Marshal.FreeHGlobal(ptr);

		_disposed = true;
	}
}

这里的Dispose(bool disposing)采用virtual,主要是为了如果子类中如果有资源需要释放,那么可以重新此virtual方法。

下面是推荐的模板方法,一个关键点是disposing变量需要传递给父类的Dispose(bool)方法。

class DerivedClassWithFinalizer : BaseClassWithFinalizer
{
    // To detect redundant calls
    private bool _disposedValue;

    ~DerivedClassWithFinalizer() => this.Dispose(false);

    // Protected implementation of Dispose pattern.
    protected override void Dispose(bool disposing)
    {
        if (!_disposedValue)
        {
            if (disposing)
            {
                // TODO: dispose managed state (managed objects).
            }

            // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below.
            // TODO: set large fields to null.
            _disposedValue = true;
        }

        // Call the base class implementation.
        base.Dispose(disposing);
    }
}

类似文章

Comments