实现 Dispose 方法

释放对象的模式(称为释放模式)对对象的生存期进行规定。 Dispose 模式仅用于访问非托管资源的对象。 这是因为垃圾回收器在回收不使用的托管对象时非常高效。

类型的 Dispose 方法应释放它拥有的所有资源。 它还应该通过调用其父类型的 Dispose 方法释放其基类型拥有的所有资源。 父类型的 Dispose 方法应该释放它拥有的所有资源,进而调用其父类型的 Dispose 方法,从而在整个基类型层次结构中传播此模式。 若要帮助确保始终正确地清理资源,Dispose 方法应该可以多次调用而不引发异常。

对只使用托管资源的类型(如数组)实现 Dispose 方法并不能提高性能,原因是这些类型由垃圾回收器自动回收。 应主要对使用本机资源的托管对象和向 .NET Framework 公开的 COM 对象使用 Dispose 方法。 使用本机资源的托管对象(如 FileStream 类)实现 IDisposable 接口。

重要说明重要事项

C++ 程序员不应该使用本主题。而应参见 Destructors and Finalizers in Visual C++。在 .NET Framework 2.0 版中,C++ 编译器为实现资源的确定性处置提供支持,并且不允许直接实现 Dispose 方法。

Dispose 方法应为它要释放的对象调用 SuppressFinalize 方法。 如果对象当前在终止队列中,则 SuppressFinalize 会阻止调用其 Finalize 方法。 请记住,执行 Finalize 方法会降低性能。 如果 Dispose 方法已完成清理对象的工作,垃圾回收器就不必调用对象的 Finalize 方法。

GC.KeepAlive 方法提供的代码示例演示了强行垃圾回收如何在回收对象的成员仍在执行时引起终结器运行。 在较长的 Dispose 方法末尾最好调用 KeepAlive 方法。

SafeHandle 备用方法

编写对象终结器的代码是一项复杂的任务,如果处理不好可能会出现问题。 因此,建议您构造 SafeHandle 对象,而非实现 Dispose 模式。

SafeHandle 类通过无中断地分配和释放句柄来简化对象生存期问题。 它包含可以保证在卸载应用程序域时运行的重要终结器。 有关使用安全句柄的优势的更多信息,请参见安全句柄和紧急终结

System.Runtime.InteropServices 命名空间中的 SafeHandle 类是操作系统句柄的抽象包装类。 从此类派生比较困难。 但可以使用 Microsoft.Win32.SafeHandles 命名空间中可提供以下项的安全句柄的派生类。

  • 文件和管道。

  • 内存视图。

  • 加密结构。

  • 注册表项。

  • 等待句柄。

示例

下面的代码示例演示为封装非托管资源的类实现 Dispose 方法的建议设计模式。

资源类通常是从复杂的本机类或 API 派生的,而且必须进行相应的自定义。 使用这一代码模式作为创建资源类的一个起始点,并根据封装的资源提供必要的自定义。

Imports System
Imports System.IO
Class Program

    Public Shared Sub Main()
        Try
            ' Initialize a Stream resource to pass 
            ' to the DisposableResource class.
           Console.Write("Enter filename and its path: ")
            Dim fileSpec As String = Console.ReadLine
            Dim fs As FileStream = File.OpenRead(fileSpec)
            Dim TestObj As DisposableResource = New DisposableResource(fs)

            ' Use the resource.
            TestObj.DoSomethingWithResource()

            ' Dispose theresource.
            TestObj.Dispose()

        Catch e As FileNotFoundException
            Console.WriteLine(e.Message)
        End Try
    End Sub
End Class

' This class shows how to use a disposable resource.
' The resource is first initialized and passed to
' the constructor, but it could also be
' initialized in the constructor.
' The lifetime of the resource does not 
' exceed the lifetime of this instance.
' This type does not need a finalizer because it does not
' directly create a native resource like a file handle
' or memory in the unmanaged heap.
Public Class DisposableResource
    Implements IDisposable

    Private _resource As Stream

    Private _disposed As Boolean

    ' The stream passed to the constructor
    ' must be readable and not null.
    Public Sub New(ByVal stream As Stream)
        MyBase.New()
        If (stream Is Nothing) Then
            Throw New ArgumentNullException("Stream is null.")
        End If
        If Not stream.CanRead Then
            Throw New ArgumentException("Stream must be readable.")
        End If
        _resource = stream
        Dim objTypeName As String = _resource.GetType.ToString
        _disposed = False
    End Sub

    ' Demonstrates using the resource.
    ' It must not be already disposed.
    Public Sub DoSomethingWithResource()
        If _disposed Then
            Throw New ObjectDisposedException("Resource was disposed.")
        End If

        ' Show the number of bytes.
        Dim numBytes As Integer = CType(_resource.Length, Integer)
        Console.WriteLine("Number of bytes: {0}", numBytes.ToString)
    End Sub

    Public Overloads Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)

        ' Use SupressFinalize in case a subclass
        ' of this type implements a finalizer.
        GC.SuppressFinalize(Me)
    End Sub

    Protected Overridable Overloads Sub Dispose(ByVal disposing As Boolean)
        If Not _disposed Then

            ' If you need thread safety, use a lock around these 
            ' operations, as well as in your methods that use the resource.
            If disposing Then
                If (Not (_resource) Is Nothing) Then
                    _resource.Dispose()
                End If
                Console.WriteLine("Object disposed.")
            End If

            ' Indicates that the instance has been disposed.
            _resource = Nothing
            _disposed = True
        End If
    End Sub
End Class
using System;
using System.IO;

class Program
{

    static void Main()
    {
        try
        {
            // Initialize a Stream resource to pass 
            // to the DisposableResource class.
            Console.Write("Enter filename and its path: ");
            string fileSpec = Console.ReadLine();
            FileStream fs = File.OpenRead(fileSpec);
            DisposableResource TestObj = new DisposableResource(fs);

            // Use the resource.
            TestObj.DoSomethingWithResource();

            // Dispose the resource.
            TestObj.Dispose();

        }
        catch (FileNotFoundException e)
        {
            Console.WriteLine(e.Message);
        }
    }
}


// This class shows how to use a disposable resource.
// The resource is first initialized and passed to
// the constructor, but it could also be
// initialized in the constructor.
// The lifetime of the resource does not 
// exceed the lifetime of this instance.
// This type does not need a finalizer because it does not
// directly create a native resource like a file handle
// or memory in the unmanaged heap.

public class DisposableResource : IDisposable
{

    private Stream _resource;  
    private bool _disposed;

    // The stream passed to the constructor 
    // must be readable and not null.
    public DisposableResource(Stream stream)
    {
        if (stream == null)
            throw new ArgumentNullException("Stream in null.");
        if (!stream.CanRead)
            throw new ArgumentException("Stream must be readable.");

        _resource = stream;

        _disposed = false;
    }

    // Demonstrates using the resource. 
    // It must not be already disposed.
    public void DoSomethingWithResource() {
        if (_disposed)
            throw new ObjectDisposedException("Resource was disposed.");

        // Show the number of bytes.
        int numBytes = (int) _resource.Length;
        Console.WriteLine("Number of bytes: {0}", numBytes.ToString());
    }


    public void Dispose() 
    {
        Dispose(true);

        // Use SupressFinalize in case a subclass
        // of this type implements a finalizer.
        GC.SuppressFinalize(this);      
    }

    protected virtual void Dispose(bool disposing)
    {
        // If you need thread safety, use a lock around these 
        // operations, as well as in your methods that use the resource.
        if (!_disposed)
        {
            if (disposing) {
                if (_resource != null)
                    _resource.Dispose();
                    Console.WriteLine("Object disposed.");
            }

            // Indicate that the instance has been disposed.
            _resource = null;
            _disposed = true;   
        }
    }
}

请参见

参考

SuppressFinalize

Destructors and Finalizers in Visual C++

实现 Finalize 和 Dispose 以清理非托管资源

概念

重写 Finalize 方法