實作 Dispose 方法

實作 Dispose 方法主要是用來釋放 Unmanaged 資源。 使用實作的 IDisposable 實例成員時,通常會串聯 Dispose 呼叫。 例如,實作 Dispose 的其他原因包括釋放配置的記憶體、移除已新增至集合的專案,或發出已取得鎖定的訊號。

.NET 垃圾收集行程不會配置或釋放 Unmanaged 記憶體。 處置物件的模式,稱為處置模式,會對物件的存留期施加順序。 處置模式用於實 IDisposable 作介面的物件,而且與檔案和管道控制碼、登錄控制碼、等候控制碼或非受控記憶體區塊的指標互動時很常見。 這是因為垃圾收集行程無法回收 Unmanaged 物件。

為了協助確保資源一律會適當地清除, Dispose 方法應該具有等冪性,如此一來,就可以多次呼叫它,而不會擲回例外狀況。 此外,後續的 Dispose 調用應該不會執行任何動作。

方法 GC.KeepAlive 所提供的程式碼範例示範垃圾收集如何造成完成項執行,而物件或其成員的 Unmanaged 參考仍在使用中。 若要讓 GC.KeepAlive 物件無法從目前常式的開頭到呼叫這個方法的點進行垃圾收集,可能很合理。

提示

對於相依性插入,在 中 IServiceCollection 註冊服務時, 服務存留期 會代表您隱含管理。 和 IServiceProvider 對應的 IHost 協調資源清除。 具體而言,和 IAsyncDisposableIDisposable 實作會在指定的存留期結束時正確處置。

如需詳細資訊,請參閱 .NET 中的相依性插入

保管庫控制碼

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

System.Runtime.InteropServices.SafeHandle是一種抽象 Managed 型別,可 System.IntPtr 包裝識別 Unmanaged 資源的 。 在 Windows,它可能會在 Unix 上識別控制碼,這是檔案描述元。 它會提供所有必要邏輯,以確保此資源一次且只有一次、處置 時 SafeHandle ,或卸載所有對 的參考 SafeHandle ,以及 SafeHandle 完成實例時。

System.Runtime.InteropServices.SafeHandle是抽象基類。 衍生類別會為不同類型的控制碼提供特定實例。 這些衍生類別會驗證 的哪些值 System.IntPtr 被視為無效,以及如何實際釋放控制碼。 例如, SafeFileHandle 衍生自 SafeHandle 來包裝 IntPtrs 以識別開啟的檔案控制碼/描述元,並覆寫其 SafeHandle.ReleaseHandle() 方法,以透過 close unix 上的函式或 CloseHandle Windows) 上的 函式關閉它 (。 .NET 程式庫中建立 Unmanaged 資源的大部分 API 都會包裝在 中 SafeHandle ,並視需要將它傳回給您,而不是傳回 SafeHandle 原始指標。 如果您在與 Unmanaged 元件互動並取得 IntPtr Unmanaged 資源的 ,您可以建立自己的 SafeHandle 類型來包裝它。 因此,少數非 SafeHandle 類型需要實作完成項;大部分可處置的模式實作最終只會包裝其他 Managed 資源,其中一些可能為 SafeHandle

Microsoft.Win32.SafeHandles 命名空間中的下列衍生類別會提供安全控制代碼:

Dispose () 和 Dispose (bool)

IDisposable 介面要求實作單一無參數方法 Dispose。 此外,任何非密封類別都應該有額外的 Dispose(bool) 多載方法。

方法簽章包括:

  • publicVisual Basic) (實作 IDisposable.Dispose 中的非虛擬 (NotOverridable) 。
  • protected virtualOverridableVisual Basic) 中的 (Dispose(bool)

Dispose () 方法

由於 Visual Basic) public 中的非虛擬 (NotOverridable ,因此當) 型別的取用者 (不再需要無參數 Dispose 方法時呼叫,其用途是釋放非受控資源、執行一般清除,以及指出完成項如果存在,就不需要執行。 釋放與 Managed 物件相關聯的實際記憶體一律是 垃圾收集行程的網域。 因此,它擁有標準實作:

public void Dispose()
{
    // Dispose of unmanaged resources.
    Dispose(true);
    // Suppress finalization.
    GC.SuppressFinalize(this);
}
Public Sub Dispose() _
    Implements IDisposable.Dispose
    ' Dispose of unmanaged resources.
    Dispose(True)
    ' Suppress finalization.
    GC.SuppressFinalize(Me)
End Sub

Dispose 方法會執行所有物件清除,所以記憶體回收行程不需要再呼叫物件的 Object.Finalize 覆寫。 因此,呼叫 SuppressFinalize 方法會防止記憶體回收行程執行完成項。 如果類型沒有完成項,則呼叫 GC.SuppressFinalize 沒有作用。 請注意,實際的清除是由 方法多載執行 Dispose(bool)

Dispose (bool) 方法多載

在多載中 disposing ,參數是 , Boolean 指出方法呼叫是否來自 Dispose 方法, (其值是) ,還是從完成項 (其值 truefalse) 。

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.
    // TODO: set large fields to null.

    _disposed = true;
}
Protected Overridable Sub Dispose(disposing As Boolean)
     If disposed Then Exit Sub	

     ' A block that frees unmanaged resources.
     
     If disposing Then
         ' Deterministic call…
         ' A conditional block that frees managed resources.    	
     End If
     
     disposed = True
End Sub

重要

disposing 完成項呼叫 時,以及 trueIDisposable.Dispose 方法呼叫 時,參數應該是 false 。 換句話說,它是 true 在具決定性地呼叫 時,以及 false 未具決定性的呼叫時。

方法的主體包含三個程式碼區塊:

  • 如果已處置物件,則為條件式傳回的區塊。

  • 釋放 Unmanaged 資源的區塊。 不論 disposing 參數的值為何,這個區塊都會執行。

  • 釋放 Managed 資源的條件性區塊。 如果 disposing 的值為 true,這個區塊就會執行。 它所釋放的 Managed 資源可能包括:

    • 實作 IDisposable 的 Managed 物件。 條件式區塊可用來呼叫其 Dispose 實作, (串聯處置) 。 如果您使用 的 System.Runtime.InteropServices.SafeHandle 衍生類別來包裝 Unmanaged 資源,您應該在這裡呼叫 SafeHandle.Dispose() 實作。

    • 耗用大量記憶體或耗用很少資源的 Managed 物件。 指派大型 Managed 物件參考, null 使其更可能無法連線。 這會比以不具決定性的方式回收它們更快。

如果方法呼叫來自完成項,則只會執行釋放 Unmanaged 資源的程式碼。 實作者負責確保 false 路徑不會與可能已處置的 Managed 物件互動。 這很重要,因為垃圾收集行程在最終處理期間處置 Managed 物件的順序不具決定性。

串聯處置呼叫

如果您的類別擁有欄位或屬性,且其類型實作 IDisposable ,則包含類別本身也應該實作 IDisposable 。 具現化實作 IDisposable 並將其儲存為實例成員的類別,也會負責清除它。 這是為了協助確保參考的可處置型別有機會透過 方法確定性地執行清除 Dispose 。 在此範例中,類別 (sealed 或在 NotInheritable Visual Basic) 中。

using System;

public sealed class Foo : IDisposable
{
    private readonly IDisposable _bar;

    public Foo()
    {
        _bar = new Bar();
    }

    public void Dispose() => _bar.Dispose();
}
Public NotInheritable Class Foo
    Implements IDisposable

    Private ReadOnly _bar As IDisposable

    Public Sub New()
        _bar = New Bar()
    End Sub

    Public Sub Dispose() Implements IDisposable.Dispose
        _bar.Dispose()
    End Sub
End Class

提示

在某些情況下,您可能會想要執行 null -checking in a finalizer (,其中包含 Dispose(false) 完成項所叫用的方法) ,其中一個主要原因是如果您不確定實例是否已完全初始化 (例如,建構函式) 中可能會擲回例外狀況。

實作處置模式

所有非密封類別 (或Visual Basic類別未修改為 NotInheritable) ,應該視為潛在的基類,因為它們可以繼承。 如果您針對任何潛在的基類實作處置模式,您必須提供下列專案:

  • 呼叫 Dispose 方法的 Dispose(bool) 實作。
  • 執行 Dispose(bool) 實際清除的方法。
  • 衍生自包裝您的 Unmanaged 資源之 SafeHandle 的類別 (建議使用),或式 Object.Finalize 方法的覆寫。 類別 SafeHandle 提供完成項,因此您不需要自行撰寫。

重要

基類只能參考 Managed 物件,並實作處置模式。 在這些情況下,不需要完成項。 只有在直接參考 Unmanaged 資源時,才需要完成項。

以下是針對使用安全控制碼之基類實作處置模式的一般模式範例。

using Microsoft.Win32.SafeHandles;
using System;
using System.Runtime.InteropServices;

class BaseClassWithSafeHandle : IDisposable
{
    // To detect redundant calls
    private bool _disposedValue;

    // Instantiate a SafeHandle instance.
    private SafeHandle _safeHandle = new SafeFileHandle(IntPtr.Zero, true);

    // 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 (!_disposedValue)
        {
            if (disposing)
            {
                _safeHandle.Dispose();
            }

            _disposedValue = true;
        }
    }
}
Imports Microsoft.Win32.SafeHandles
Imports System.Runtime.InteropServices

Class BaseClassWithSafeHandle : Implements IDisposable
    ' Flag: Has Dispose already been called?
    Dim disposed As Boolean = False
    ' Instantiate a SafeHandle instance.
    Dim handle As SafeHandle = New SafeFileHandle(IntPtr.Zero, True)

    ' Public implementation of Dispose pattern callable by consumers.
    Public Sub Dispose() _
               Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub

    ' Protected implementation of Dispose pattern.
    Protected Overridable Sub Dispose(disposing As Boolean)
        If disposed Then Return

        If disposing Then
            handle.Dispose()
        End If

        disposed = True
    End Sub
End Class

注意

上一個範例使用 SafeFileHandle 物件來說明模式;可改用任何衍生自 SafeHandle 的物件。 請注意,該範例未正確地執行個體化其 SafeFileHandle 物件。

以下一般模式將會實作覆寫 Object.Finalize 之基底類別的處置模式。

using System;

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

    ~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 (!_disposedValue)
        {
            if (disposing)
            {
                // TODO: dispose managed state (managed objects)
            }

            // TODO: free unmanaged resources (unmanaged objects) and override finalizer
            // TODO: set large fields to null
            _disposedValue = true;
        }
    }
}
Class BaseClassWithFinalizer : Implements IDisposable
    ' Flag: Has Dispose already been called?
    Dim disposed As Boolean = False

    ' Public implementation of Dispose pattern callable by consumers.
    Public Sub Dispose() _
               Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub

    ' Protected implementation of Dispose pattern.
    Protected Overridable Sub Dispose(disposing As Boolean)
        If disposed Then Return

        If disposing Then
            ' Dispose managed objects that implement IDisposable.
            ' Assign null to managed objects that consume large amounts of memory or consume scarce resources.
        End If

        ' Free any unmanaged objects here.
        '
        disposed = True
    End Sub

    Protected Overrides Sub Finalize()
        Dispose(False)
    End Sub
End Class

提示

在 C# 中,您會藉由提供 完成項來實作最終化,而不是覆寫 Object.Finalize 。 在 Visual Basic中,您會使用 Protected Overrides Sub Finalize() 建立完成項。

實作衍生類別的處置模式

從實作 IDisposable 介面的類別衍生的類別不應該實作 IDisposable,因為 IDisposable.Dispose 的基底類別實作會由其衍生類別繼承。 相反地,若要清除衍生類別,請提供下列專案:

  • protected override void Dispose(bool) 寫基類方法並執行衍生類別的實際清除的方法。 這個方法也必須呼叫 base.Dispose(bool) Visual Basic) 方法中的 (MyBase.Dispose(bool) ,並將處置狀態 (bool disposing 參數) 做為引數。
  • 衍生自包裝您的 Unmanaged 資源之 SafeHandle 的類別 (建議使用),或式 Object.Finalize 方法的覆寫。 SafeHandle 類別會提供完成項,讓您不必自行撰寫程式碼。 如果您確實提供完成項,則必須使用 false 引數呼叫 Dispose(bool) 多載。

以下是針對使用安全控制碼之衍生類別實作處置模式的一般模式範例:

using Microsoft.Win32.SafeHandles;
using System;
using System.Runtime.InteropServices;

class DerivedClassWithSafeHandle : BaseClassWithSafeHandle
{
    // To detect redundant calls
    private bool _disposedValue;

    // Instantiate a SafeHandle instance.
    private SafeHandle _safeHandle = new SafeFileHandle(IntPtr.Zero, true);

    // Protected implementation of Dispose pattern.
    protected override void Dispose(bool disposing)
    {
        if (!_disposedValue)
        {
            if (disposing)
            {
                _safeHandle.Dispose();
            }

            _disposedValue = true;
        }

        // Call base class implementation.
        base.Dispose(disposing);
    }
}
Imports Microsoft.Win32.SafeHandles
Imports System.Runtime.InteropServices

Class DerivedClassWithSafeHandle : Inherits BaseClassWithSafeHandle
    ' Flag: Has Dispose already been called?
    Dim disposed As Boolean = False
    ' Instantiate a SafeHandle instance.
    Dim handle As SafeHandle = New SafeFileHandle(IntPtr.Zero, True)

    ' Protected implementation of Dispose pattern.
    Protected Overrides Sub Dispose(disposing As Boolean)
        If disposed Then Return

        If disposing Then
            handle.Dispose()
            ' Free any other managed objects here.
            '
        End If

        ' Free any unmanaged objects here.
        '
        disposed = True

        ' Call base class implementation.
        MyBase.Dispose(disposing)
    End Sub
End Class

注意

上一個範例使用 SafeFileHandle 物件來說明模式;可改用任何衍生自 SafeHandle 的物件。 請注意,該範例未正確地執行個體化其 SafeFileHandle 物件。

以下一般模式將會實作覆寫 Object.Finalize 之衍生類別的處置模式:

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);
    }
}
Class DerivedClassWithFinalizer : Inherits BaseClassWithFinalizer
    ' Flag: Has Dispose already been called?
    Dim disposed As Boolean = False

    ' Protected implementation of Dispose pattern.
    Protected Overrides Sub Dispose(disposing As Boolean)
        If disposed Then Return

        If disposing Then
            ' Dispose managed objects that implement IDisposable.
            ' Assign null to managed objects that consume large amounts of memory or consume scarce resources.
        End If

        ' Free any unmanaged objects here.
        '
        disposed = True

        ' Call the base class implementation.
        MyBase.Dispose(disposing)
    End Sub

    Protected Overrides Sub Finalize()
        Dispose(False)
    End Sub
End Class

另請參閱