實作 Dispose 方法

處置物件的模式稱為處置模式,會在物件的存留期上安排順序。 處置模式僅針對存取 Unmanaged 資源的物件使用。 這是因為記憶體回收行程在回收未使用的 Managed 物件時相當有效率。

型別的 Dispose 方法應該釋放它所擁有的所有資源, 它也應該藉由呼叫其基底型別 (Base Type) 之父型別的 Dispose 方法,來釋放其基底型別所擁有的所有資源。 父型別的 Dispose 方法應釋放它擁有的所有資源,接著呼叫其父型別的 Dispose 方法,將此模式傳播到整個基底型別的階層架構中。 若要確保資源永遠都能適當清除,Dispose 方法應該能夠被呼叫多次而不會擲回例外狀況。

在僅使用 Managed 資源 (例如陣列) 的型別上實作 Dispose 方法不會有任何效能益處,因為記憶體回收行程會自動回收這些資源。 主要在使用原生資源的 Managed 物件上和對 .NET Framework 公開的 COM 物件上使用 Dispose 方法。 使用原生資源 (例如 FileStream 類別) 的 Managed 物件會實作 IDisposable 介面。

重要事項重要事項

C++ 程式設計人員不應該使用這個說明主題,而應該參閱 Destructors and Finalizers in Visual C++。在 .NET Framework 2.0 版中,C++ 編譯器可讓您實作決定性的資源處置,但不允許直接實作 Dispose 方法。

Dispose 方法應該呼叫它正在處置 (Dispose) 之物件的 SuppressFinalize 方法。 如果物件目前位於最終處理佇列上,則 SuppressFinalize 會避免呼叫其 Finalize 方法。 請記住,執行 Finalize 方法將付出很大的效能代價。 如果您的 Dispose 方法已完成清理物件的工作,那麼就記憶體回收行程就不需要呼叫物件的 Finalize 方法。

此處所提供的 GC.KeepAlive 方法程式碼範例,顯示積極記憶體回收如何在被回收的物件仍在執行時,即讓完成項開始執行。 在冗長的 Dispose 方法結尾處呼叫 KeepAlive 方法,這是個不錯的做法。

SafeHandle 替代方法

撰寫物件完成項的程式碼是一項複雜的工作,若未正確撰寫,可能會造成問題。 因此,建議您建構 SafeHandle 物件,而不要實作處置模式。

SafeHandle 類別會在不受干擾的情況下,藉由指派和釋放控制代碼的方式簡化物件存留期的問題。 它包含重要的完成項,該完成項保證會在應用程式定義域卸載時執行。 如需使用安全控制代碼之優點的詳細資訊,請參閱安全控制代碼和關鍵結束

System.Runtime.InteropServices 命名空間中的 SafeHandle 類別是作業系統控制代碼的抽象包裝函式類別。 從這個類別衍生並不容易。 請改用 Microsoft.Win32.SafeHandles 命名空間中的衍生類別,這些類別可為下列各項提供安全的控制代碼:

  • 檔案和管道。

  • 記憶體檢視。

  • 加密結構。

  • 登錄機碼。

  • 等候控制代碼。

範例

以下程式碼範例說明針對封裝 Unmanaged 資源的類別實作 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 以清除 Unmanaged 資源

概念

覆寫 Finalize 方法