共用方式為


實作 Dispose 方法

Dispose 方法主要是為了釋放 Unmanaged 資源而實作。 使用 IDisposable 實作的實例成員時,通常會連續呼叫 Dispose。 執行 Dispose還有其他原因,例如,要釋放已配置的記憶體、移除新增至集合的項目,或發出釋放已取得鎖定的信號。

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

為了協助確保資源獲得適當清理,Dispose 方法應該具有等冪性,如此便能多次呼叫,而不會擲回例外狀況。 此外,後續 Dispose 調用應該不會執行任何動作。

GC.KeepAlive 方法提供的程式碼範例顯示垃圾收集器如何在物件或其成員的非受管參考仍在使用時執行終結器。 利用 GC.KeepAlive 從目前程式的開始到呼叫該方法的位置,使該對象不符合垃圾回收的條件。

小技巧

就相依性注入而言,當您在 IServiceCollection中註冊服務時,會代表您隱含管理 服務存留期IServiceProvider 和對應的 IHost 協調資源清理。 具體來說,IDisposableIAsyncDisposable 的實作會在指定的使用週期結束時正確處置。

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

安全把手

撰寫物件完成項的程式代碼是一項複雜的工作,如果未正確完成,可能會導致問題。 因此,我們建議您建構 System.Runtime.InteropServices.SafeHandle 物件,而不是實作終結器。

System.Runtime.InteropServices.SafeHandle 是一種抽象的管理型別,封裝識別非受控資源的 System.IntPtr。 在 Windows 上,它可能會識別句柄,並在 Unix 上識別檔案描述元。 SafeHandle 提供必要的所有邏輯,以確保此資源僅在一次且只有一次被釋放,無論是在 SafeHandle 被釋放時,還是當對 SafeHandle 的所有引用被移除且 SafeHandle 實例完成終結時。

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

Microsoft.Win32.SafeHandles 命名空間中的下列衍生類別提供安全句柄。

班級 其保留的資源
SafeFileHandle
SafeMemoryMappedFileHandle
SafePipeHandle
檔案、記憶體對應檔案和管線
SafeMemoryMappedViewHandle 內存檢視
SafeNCryptKeyHandle
SafeNCryptProviderHandle
SafeNCryptSecretHandle
密碼編譯建構
SafeRegistryHandle 登錄機碼
SafeWaitHandle 等候句柄

Dispose() 和 Dispose(bool)

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

方法簽章如下:

  • public 非虛擬(在 Visual Basic 中為NotOverridable)(IDisposable.Dispose 實作)。
  • protected virtual (Visual Basic 中的OverridableDispose(bool)

Dispose() 方法

因為當 public的非虛擬(Visual Basic 中的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 方法(其值是 true),還是來自完成項(其值為 false)。

protected virtual void Dispose(bool disposing)
{
    if (_disposed)
    {
        return;
    }

    if (disposing)
    {
        // Dispose managed state (managed objects).
        // ...
    }

    // Free unmanaged resources.
    // ...

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

     If disposing Then

         ' Free managed resources.
         ' ...

     End If

     ' Free unmanaged resources.
     ' ...

     disposed = True
End Sub

重要

當從完成項呼叫時,disposing 參數應該是 false,而當從 IDisposable.Dispose 方法呼叫時,應該是 true。 換句話說,當以決定性方式呼叫 時,true,並在非決定性呼叫時 false

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

  • 如果物件已經被處置,則這是一個用於條件式返回的區塊。

  • 釋放受控資源的條件式區塊。 如果 disposing 的值是 true,這個區塊就會執行。 其釋放的受控資源可以包括:

    • 實作 IDisposable的管理物件。 條件式區塊可用來呼叫其 Dispose 實作(層疊釋放)。 如果您使用 System.Runtime.InteropServices.SafeHandle 衍生類別來包裝非受控資源,您應該在這裡呼叫 SafeHandle.Dispose() 的實作。
    • Managed物件是使用大量記憶體或耗用稀缺資源的物件。 將大型 Managed 物件參考指派給 null,使其更可能無法到達。 這會比不具決定性的回收更快釋放它們。
  • 釋放非受控資源的區塊。 不論 disposing 參數的值為何,此區塊都會執行。

如果方法調用來自終結器,則應僅執行釋放非受控資源的程式碼。 執行者負責確保虛假路徑不會與可能已處置的受管理物件互動。 這很重要,因為垃圾收集器在最終處理期間處置受控對象的順序不具決定性。

串聯處置呼叫

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

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

提示

  • 如果您的類別具有 IDisposable 字段或屬性,但未 自己,這表示類別不會建立物件,則類別不需要實作 IDisposable
  • 在某些情況下,您可能會想要在完成項中執行 null檢查(這包括由完成項叫用的 Dispose(false) 方法)。 其中一個主要原因是,如果不確定實例是否已完全初始化(例如,在建構函式中可能會拋出例外)。

實作釋放模式

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

  • 呼叫 Dispose(bool) 方法的 Dispose 實作。
  • 執行實際清除的 Dispose(bool) 方法。
  • 一個衍生自 SafeHandle 的類別,用於包裝您的非受控資源(建議),或者覆寫 Object.Finalize 方法。 SafeHandle 類別提供了終結器,因此您不需要自行撰寫。

重要

可能基類只參考受控物件並實作處置模式。 在這些情況下,不需要終結器。 只有在您直接參考非託管資源時,才需要終結器。

以下是一個示範如何在基類中實作使用安全控制代碼的處置模式的一般範例。

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

public 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);
        GC.SuppressFinalize(this);
    }

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

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

Public Class BaseClassWithSafeHandle
    Implements IDisposable

    ' To detect redundant calls
    Private _disposedValue As Boolean

    ' Instantiate a SafeHandle instance.
    Private _safeHandle 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(ByVal disposing As Boolean)
        If Not _disposedValue Then

            If disposing Then
                _safeHandle?.Dispose()
                _safeHandle = Nothing
            End If

            _disposedValue = True
        End If
    End Sub
End Class

注意

上一個範例會使用 SafeFileHandle 對象來說明模式;任何衍生自 SafeHandle 的物件都可以改用。 請注意,此範例無法正確具現化其 SafeFileHandle 物件。

這是覆寫 Object.Finalize的基類實施 dispose 模式的一般模式。

using System;

public 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;
        }
    }
}
Public Class BaseClassWithFinalizer
    Implements IDisposable

    ' To detect redundant calls
    Private _disposedValue As Boolean

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

    ' 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(ByVal disposing As Boolean)
        If Not _disposedValue Then

            If disposing Then
                ' TODO: dispose managed state (managed objects)
            End If

            ' TODO free unmanaged resources (unmanaged objects) And override finalizer
            ' TODO: set large fields to null
            _disposedValue = True
        End If
    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 參數) 傳遞為自變數。
  • 衍生自 SafeHandle 的類別來包裝您的非受控資源(建議),或者覆寫 Object.Finalize 方法。 SafeHandle 類別提供一個完成項,讓您不必撰寫程序代碼。 如果您確實提供了一個終結器,則必須使用 false 參數來呼叫 Dispose(bool) 方法多載。

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

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

public 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();
                _safeHandle = null;
            }

            _disposedValue = true;
        }

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

Public Class DerivedClassWithSafeHandle
    Inherits BaseClassWithSafeHandle

    ' To detect redundant calls
    Private _disposedValue As Boolean

    ' Instantiate a SafeHandle instance.
    Private _safeHandle As SafeHandle = New SafeFileHandle(IntPtr.Zero, True)

    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
        If Not _disposedValue Then

            If disposing Then
                _safeHandle?.Dispose()
                _safeHandle = Nothing
            End If

            _disposedValue = True
        End If

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

注意

上一個範例會使用 SafeFileHandle 對象來說明模式;任何衍生自 SafeHandle 的物件都可以改用。 請注意,此範例無法正確具現化其 SafeFileHandle 物件。

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

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

    ~DerivedClassWithFinalizer() => 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);
    }
}
Public Class DerivedClassWithFinalizer
    Inherits BaseClassWithFinalizer

    ' To detect redundant calls
    Private _disposedValue As Boolean

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

    ' Protected implementation of Dispose pattern.
    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
        If Not _disposedValue Then

            If disposing Then
                ' TODO: dispose managed state (managed objects).
            End If

            ' TODO free unmanaged resources (unmanaged objects) And override a finalizer below.
            ' TODO: set large fields to null.
            _disposedValue = True
        End If

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

另請參閱