實作 Dispose 方法Implement a Dispose method

執行 Dispose 方法主要是用來釋放程式碼所使用的非受控資源。Implementing the Dispose method is primarily for releasing unmanaged resources used by your code. 使用實作為實作為實例成員時 IDisposable ,通常會使用 cascade Dispose 呼叫。When working with instance members that are IDisposable implementations, it's common to cascade Dispose calls. 有其他的原因需要執行 Dispose ,例如復原先前完成的專案。There are additional reasons for implementing Dispose, such as undoing something that was previously done. 例如,釋放已配置的記憶體、從已新增的集合中移除專案、發出已取得鎖定的釋放信號等等。For example, freeing memory that was allocated, removing an item from a collection that was added, signaling the release of a lock that was acquired, and so on.

.Net 垃圾收集行程不會配置或釋放非受控記憶體。The .NET garbage collector does not allocate or release unmanaged memory. 處置物件的模式稱為處置模式,會在物件的存留期加上順序。The pattern for disposing an object, referred to as the dispose pattern, imposes order on the lifetime of an object. 處置模式用於實作為介面的物件 IDisposable ,而且與檔案和管道控制碼、登錄控制碼、等候控制碼或非受控記憶體區塊的指標互動時很常見。The dispose pattern is used for objects that implement the IDisposable interface, and is common when interacting with file and pipe handles, registry handles, wait handles, or pointers to blocks of unmanaged memory. 這是因為垃圾收集行程無法回收未受管理的物件。This is because the garbage collector is unable to reclaim unmanaged objects.

為了協助確保一律適當地清除資源, Dispose 方法應該具有等冪性,讓它可以呼叫多次,而不會擲回例外狀況。To help ensure that resources are always cleaned up appropriately, a Dispose method should be idempotent, such that it is callable multiple times without throwing an exception. 此外,的後續調用 Dispose 也不會執行任何動作。Furthermore, subsequent invocations of Dispose should do nothing.

針對方法提供的程式碼範例會 GC.KeepAlive 顯示垃圾收集如何導致完成項執行,而物件或其成員的非受控參考仍在使用中。The code example provided for the GC.KeepAlive method shows how garbage collection can cause a finalizer to run, while an unmanaged reference to the object or its members is still in use. 利用 GC.KeepAlive ,讓物件無法從目前常式的開始進行垃圾收集,到呼叫這個方法的時間點,是合理的作法。It may make sense to utilize GC.KeepAlive to make the object ineligible for garbage collection from the start of the current routine to the point where this method is called.

安全控制碼Safe handles

撰寫物件完成項的程式碼是一項複雜的工作,若未正確撰寫,可能會造成問題。Writing code for an object's finalizer is a complex task that can cause problems if not done correctly. 因此,建議您建構 System.Runtime.InteropServices.SafeHandle 物件,而不要實作完成項。Therefore, we recommend that you construct System.Runtime.InteropServices.SafeHandle objects instead of implementing a finalizer.

System.Runtime.InteropServices.SafeHandle是一個抽象的 managed 型別,它會包裝 System.IntPtr 可識別非受控資源的。A System.Runtime.InteropServices.SafeHandle is an abstract managed type that wraps an System.IntPtr that identifies an unmanaged resource. 在 Windows 上,it 可能會在 Unix 上識別控制碼,也就是檔案描述項。On Windows it might identify a handle while on Unix, a file descriptor. 它會提供所有必要的邏輯,確保此資源只會釋出一次,或在已卸載 SafeHandle 所有的參考 SafeHandle ,且已完成實例時才釋放 SafeHandleIt provides all of the logic necessary to ensure that this resource is released once and only once, when the SafeHandle is disposed of or when all references to the SafeHandle have been dropped and the SafeHandle instance is finalized.

System.Runtime.InteropServices.SafeHandle是抽象基類。The System.Runtime.InteropServices.SafeHandle is an abstract base class. 衍生類別會針對不同類型的控制碼提供特定的實例。Derived classes provide specific instances for different kinds of handle. 這些衍生的類別會驗證哪些值 System.IntPtr 被視為無效,以及如何實際釋放控制碼。These derived classes validate what values for the System.IntPtr are considered invalid and how to actually free the handle. 例如, SafeFileHandle 衍生自 SafeHandle 來包裝以 IntPtrs 識別開啟的檔案控制代碼/描述元,並覆寫其 SafeHandle.ReleaseHandle() 方法來關閉它(透過 Unix 上的函式 close 或 Windows 上的函式 CloseHandle )。For example, SafeFileHandle derives from SafeHandle to wrap IntPtrs that identify open file handles/descriptors, and overrides its SafeHandle.ReleaseHandle() method to close it (via the close function on Unix or CloseHandle function on Windows). .NET 程式庫中建立非受控資源的大部分 Api 會將它包裝在中, SafeHandleSafeHandle 視需要傳回給您,而不是處理原始指標。Most APIs in .NET libraries that create an unmanaged resource will wrap it in a SafeHandle and return that SafeHandle to you as needed, rather than handing back the raw pointer. 在您與非受控元件互動並取得 IntPtr 非受控資源的情況下,您可以建立自己的 SafeHandle 類型來包裝它。In situations where you interact with an unmanaged component and get an IntPtr for an unmanaged resource, you can create your own SafeHandle type to wrap it. 因此,少數的非型別 SafeHandle 需要實作為完成項; 大部分可處置的模式執行只會包裝其他 managed 資源,其中有些可能是 SafeHandle s。As a result, few non-SafeHandle types need to implement finalizers; most disposable pattern implementations only end up wrapping other managed resources, some of which may be SafeHandles.

Microsoft.Win32.SafeHandles 命名空間中的下列衍生類別會提供安全控制代碼:The following derived classes in the Microsoft.Win32.SafeHandles namespace provide safe handles:

Dispose ()和 Dispose (bool)Dispose() and Dispose(bool)

IDisposable 介面要求實作單一無參數方法 DisposeThe IDisposable interface requires the implementation of a single parameterless method, Dispose. 此外,任何非密封的類別都應該要有額外的多載 Dispose(bool) 方法來執行:Also, any non-sealed class should have an additional Dispose(bool) overload method to be implemented:

  • 沒有 public 任何參數的非虛擬( NonInheritable 在 Visual Basic 中) IDisposable.Dispose 執行。A public non-virtual (NonInheritable in Visual Basic) IDisposable.Dispose implementation that has no parameters.

  • protected virtualOverridable 在 Visual Basic) Dispose 方法,其簽章為:A protected virtual (Overridable in Visual Basic) Dispose method whose signature is:

    protected virtual void Dispose(bool disposing)
    {
    }
    
    Protected Overridable Sub Dispose(disposing As Boolean)
    End Sub
    

    重要

    disposing false 從完成項呼叫時,以及 true 從方法呼叫時,參數應該是 IDisposable.DisposeThe disposing parameter should be false when called from a finalizer, and true when called from the IDisposable.Dispose method. 換句話說,它是以決定性的方式 true 呼叫,並在不具決定性的情況下呼叫 falseIn other words, it is true when deterministically called and false when non-deterministically called.

Dispose ()方法The Dispose() method

因為 public 非虛擬( NonInheritable 在 Visual Basic)中,無參數 Dispose 方法是由類型的取用者呼叫,其目的是要釋放非受控資源、執行一般清除,並指出完成項(如果有的話)不需要執行。Because the public, non-virtual (NonInheritable in Visual Basic), parameterless Dispose method is called by a consumer of the type, its purpose is to free unmanaged resources, perform general cleanup, and to indicate that the finalizer, if one is present, doesn't have to run. 釋放與 managed 物件相關聯的實際記憶體一律是垃圾收集行程的網域。Freeing the actual memory associated with a managed object is always the domain of the garbage collector. 因此,它擁有標準實作:Because of this, it has a standard implementation:

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 覆寫。The Dispose method performs all object cleanup, so the garbage collector no longer needs to call the objects' Object.Finalize override. 因此,呼叫 SuppressFinalize 方法會防止記憶體回收行程執行完成項。Therefore, the call to the SuppressFinalize method prevents the garbage collector from running the finalizer. 如果類型沒有完成項,則呼叫 GC.SuppressFinalize 沒有作用。If the type has no finalizer, the call to GC.SuppressFinalize has no effect. 請注意,方法多載會執行實際的清除作業 Dispose(bool)Note that the actual cleanup is performed by the Dispose(bool) method overload.

Dispose (bool)方法多載The Dispose(bool) method overload

在多載中, disposing 參數為 Boolean ,指出方法呼叫是來自 Dispose 方法(其值為 true )或來自完成項(其值為 false )。In the overload, the disposing parameter is a Boolean that indicates whether the method call comes from a Dispose method (its value is true) or from a finalizer (its value is false).

方法的主體是由兩個程式碼區塊所構成:The body of the method consists of two blocks of code:

  • 釋放 Unmanaged 資源的區塊。A block that frees unmanaged resources. 不論 disposing 參數的值為何,這個區塊都會執行。This block executes regardless of the value of the disposing parameter.

  • 釋放 Managed 資源的條件性區塊。A conditional block that frees managed resources. 如果 disposing 的值為 true,這個區塊就會執行。This block executes if the value of disposing is true. 它所釋放的 Managed 資源可能包括:The managed resources that it frees can include:

    • 實作 IDisposable 的受控物件。Managed objects that implement IDisposable. 條件式區塊可以用來呼叫其 Dispose 實作為(cascade dispose)。The conditional block can be used to call their Dispose implementation (cascade dispose). 如果您已使用的衍生類別 System.Runtime.InteropServices.SafeHandle 來包裝您的非受控資源,您應該在 SafeHandle.Dispose() 這裡呼叫執行。If you have used a derived class of System.Runtime.InteropServices.SafeHandle to wrap your unmanaged resource, you should call the SafeHandle.Dispose() implementation here.

    • 耗用大量記憶體或耗用少量資源的 Managed 物件。Managed objects that consume large amounts of memory or consume scarce resources. 將大型 managed 物件參考指派給, null 使其更容易到達。Assign large managed object references to null to make them more likely to be unreachable. 比起以非決定性的方式回收,這會比更快地釋放它們。This releases them faster than if they were reclaimed non-deterministically.

如果方法呼叫來自完成項,則只會執行釋放非受控資源的程式碼。If the method call comes from a finalizer, only the code that frees unmanaged resources should execute. 實施者負責確保錯誤路徑不會與可能已回收的受管理物件互動。The implementer is responsible for ensuring the the false path doesn't interact with managed objects that may have been reclaimed. 這很重要,因為垃圾收集行程在最終處理期間終結 managed 物件的順序不具決定性。This is important because the order in which the garbage collector destroys managed objects during finalization is non-deterministic.

Cascade dispose 呼叫Cascade dispose calls

如果您的類別擁有欄位或屬性,而且它的型別為 IDisposable ,則包含類別本身也應該會執行 IDisposableIf your class owns a field or property, and its type implements IDisposable, the containing class itself should also implement IDisposable. IDisposable 現化實作為實例成員的類別,也會負責其清除。A class that instantiates an IDisposable implementation and storing it as an instance member, is also responsible for its cleanup. 這是為了協助確保參考的可處置型別有機會透過方法,以決定性的方式執行清除 DisposeThis is to help ensure that the referenced disposable types are given the opportunity to deterministically perform clean up through the Dispose method. 在此範例中,類別是 sealed (或 NotInheritable Visual Basic 中的)。In this example, the class is sealed (or NotInheritable in Visual Basic).

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

執行處置模式Implement the dispose pattern

所有非密封類別或(Visual Basic 類別未修改為 NotInheritable )都應該視為可能的基類,因為它們可以被繼承。All non-sealed classes or (Visual Basic classes not modified as NotInheritable) should be considered a potential base class, because they could be inherited. 如果您針對任何可能的基類執行處置模式,您必須提供下列各項:If you implement the dispose pattern for any potential base class, you must provide the following:

  • 呼叫 Dispose 方法的 Dispose(bool) 實作。A Dispose implementation that calls the Dispose(bool) method.
  • Dispose(bool)執行實際清除的方法。A Dispose(bool) method that performs the actual cleanup.
  • 衍生自包裝您的 Unmanaged 資源之 SafeHandle 的類別 (建議使用),或式 Object.Finalize 方法的覆寫。Either a class derived from SafeHandle that wraps your unmanaged resource (recommended), or an override to the Object.Finalize method. SafeHandle類別會提供完成項,因此您不需要自行撰寫。The SafeHandle class provides a finalizer, so you do not have to write one yourself.

重要

基類可能只會參考 managed 物件,並實作為處置模式。It is possible for a base class to only reference managed objects, and implement the dispose pattern. 在這些情況下,不需要完成項。In these cases, a finalizer is unnecessary. 只有當您直接參考非受控資源時,才需要完成項。A finalizer is only required if you directly reference unmanaged resources.

以下一般模式將會實作使用安全控制代碼之基底類別的處置模式。Here's the general pattern for implementing the dispose pattern for a base class that uses a safe handle.

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

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

    // 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 (_disposed)
        {
            return;
        }

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

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

Class BaseClass : 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()
            ' Free any other managed objects here.
            '
        End If

        disposed = True
    End Sub
End Class

注意

上一個範例使用 SafeFileHandle 物件來說明模式;可改用任何衍生自 SafeHandle 的物件。The previous example uses a SafeFileHandle object to illustrate the pattern; any object derived from SafeHandle could be used instead. 請注意,該範例未正確地執行個體化其 SafeFileHandle 物件。Note that the example does not properly instantiate its SafeFileHandle object.

以下一般模式將會實作覆寫 Object.Finalize 之基底類別的處置模式。Here's the general pattern for implementing the dispose pattern for a base class that overrides Object.Finalize.

using System;

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

    ~BaseClass() => 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.
        // TODO: set large fields to null.

        _disposed = true;
    }
}
Class BaseClass : 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
            ' Free any other managed objects here.
            '
        End If

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

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

提示

在 c # 中,您可以藉由覆寫來建立完成項 Object.FinalizeIn C#, you create a finalizer by overriding Object.Finalize. 在 Visual Basic 中,這是使用來完成 Protected Overrides Sub Finalize()In Visual Basic, this is done with Protected Overrides Sub Finalize().

執行衍生類別的處置模式Implement the dispose pattern for a derived class

從實作 IDisposable 介面的類別衍生的類別不應該實作 IDisposable,因為 IDisposable.Dispose 的基底類別實作會由其衍生類別繼承。A class derived from a class that implements the IDisposable interface shouldn't implement IDisposable, because the base class implementation of IDisposable.Dispose is inherited by its derived classes. 相反地,若要清除衍生類別,請提供下列內容:Instead, to cleanup a derived class, you provide the following:

  • 一種 protected override void Dispose(bool) 方法,會覆寫基類方法,並執行衍生類別的實際清除。A protected override void Dispose(bool) method that overrides the base class method and performs the actual cleanup of the derived class. 這個方法也必須呼叫 base.Dispose(bool) 基類的( MyBase.Dispose(bool) 在 Visual Basic)方法中,並傳遞引數的處置狀態。This method must also call the base.Dispose(bool) (MyBase.Dispose(bool) in Visual Basic) method of the base class and pass its disposing status for the argument.
  • 衍生自包裝您的 Unmanaged 資源之 SafeHandle 的類別 (建議使用),或式 Object.Finalize 方法的覆寫。Either a class derived from SafeHandle that wraps your unmanaged resource (recommended), or an override to the Object.Finalize method. SafeHandle 類別會提供完成項,讓您不必自行撰寫程式碼。The SafeHandle class provides a finalizer that frees you from having to code one. 如果您提供完成項,它必須呼叫具有之 Dispose(bool) disposing 引數的多載 falseIf you do provide a finalizer, it must call the Dispose(bool) overload with a disposing argument of false.

以下一般模式將會實作使用安全控制代碼之衍生類別的處置模式:Here's the general pattern for implementing the dispose pattern for a derived class that uses a safe handle:

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

class DerivedClass : BaseClass
{
    // To detect redundant calls
    private bool _disposed = false;

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

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

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

        _disposed = true;

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

Class DerivedClass : Inherits BaseClass
    ' 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 的物件。The previous example uses a SafeFileHandle object to illustrate the pattern; any object derived from SafeHandle could be used instead. 請注意,該範例未正確地執行個體化其 SafeFileHandle 物件。Note that the example does not properly instantiate its SafeFileHandle object.

以下一般模式將會實作覆寫 Object.Finalize 之衍生類別的處置模式:Here's the general pattern for implementing the dispose pattern for a derived class that overrides Object.Finalize:

using System;

class DerivedClass : BaseClass
{
    // To detect redundant calls
    bool _disposed = false;

    ~DerivedClass() => Dispose(false);

    // Protected implementation of Dispose pattern.
    protected override 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;

        // Call the base class implementation.
        base.Dispose(disposing);
    }
}
Class DerivedClass : Inherits BaseClass
    ' 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
            ' Free any other managed objects here.
            '
        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

使用安全控制碼來執行處置模式Implement the dispose pattern with safe handles

下列範例將說明使用安全控制代碼封裝 Unmanaged 資源之基底類別 DisposableStreamResource 的處置模式。The following example illustrates the dispose pattern for a base class, DisposableStreamResource, that uses a safe handle to encapsulate unmanaged resources. 它會定義 DisposableStreamResource 類別,該類別使用 SafeFileHandle 包裝代表開啟檔案的 Stream 物件。It defines a DisposableStreamResource class that uses a SafeFileHandle to wrap a Stream object that represents an open file. 類別也包含單一屬性, Size 它會傳回檔案資料流程中的總位元組數。The class also includes a single property, Size, that returns the total number of bytes in the file stream.

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

public class DisposableStreamResource : IDisposable
{
    // Define constants.
    protected const uint GENERIC_READ = 0x80000000;
    protected const uint FILE_SHARE_READ = 0x00000001;
    protected const uint OPEN_EXISTING = 3;
    protected const uint FILE_ATTRIBUTE_NORMAL = 0x80;
    private const int INVALID_FILE_SIZE = unchecked((int)0xFFFFFFFF);

    // Define Windows APIs.
    [DllImport("kernel32.dll", EntryPoint = "CreateFileW", CharSet = CharSet.Unicode)]
    protected static extern SafeFileHandle CreateFile(
        string lpFileName, uint dwDesiredAccess,
        uint dwShareMode, IntPtr lpSecurityAttributes,
        uint dwCreationDisposition, uint dwFlagsAndAttributes,
        IntPtr hTemplateFile);

    [DllImport("kernel32.dll")]
    private static extern int GetFileSize(
        SafeFileHandle hFile, out int lpFileSizeHigh);

    // Define locals.
    private bool _disposed = false;
    private readonly SafeFileHandle _safeHandle;
    private readonly int _upperWord;

    public DisposableStreamResource(string fileName)
    {
        if (string.IsNullOrWhiteSpace(fileName))
        {
            throw new ArgumentException("The fileName cannot be null or an empty string");
        }

        _safeHandle = CreateFile(
            fileName, GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero,
            OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);

        // Get file size.
        Size = GetFileSize(_safeHandle, out _upperWord);
        if (Size == INVALID_FILE_SIZE)
        {
            Size = -1;
        }
        else if (_upperWord > 0)
        {
            Size = (((long)_upperWord) << 32) + Size;
        }
    }

    public long Size { get; }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

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

        // Dispose of managed resources here.
        if (disposing)
        {
            _safeHandle?.Dispose();
        }

        // Dispose of any unmanaged resources not wrapped in safe handles.

        _disposed = true;
    }
}
Imports Microsoft.Win32.SafeHandles
Imports System.IO

Public Class DisposableStreamResource : Implements IDisposable
    ' Define constants.
    Protected Const GENERIC_READ As UInteger = &H80000000UI
    Protected Const FILE_SHARE_READ As UInteger = &H0I
    Protected Const OPEN_EXISTING As UInteger = 3
    Protected Const FILE_ATTRIBUTE_NORMAL As UInteger = &H80
    Private Const INVALID_FILE_SIZE As Integer = &HFFFFFFFF

    ' Define Windows APIs.
    Protected Declare Function CreateFile Lib "kernel32" Alias "CreateFileA" (
        lpFileName As String, dwDesiredAccess As UInt32,
        dwShareMode As UInt32, lpSecurityAttributes As IntPtr,
        dwCreationDisposition As UInt32, dwFlagsAndAttributes As UInt32,
        hTemplateFile As IntPtr) As SafeFileHandle

    Private Declare Function GetFileSize Lib "kernel32" (
        hFile As SafeFileHandle, ByRef lpFileSizeHigh As Integer) As Integer

    ' Define locals.
    Private disposed As Boolean = False
    Private ReadOnly safeHandle As SafeFileHandle
    Private ReadOnly upperWord As Integer

    Public Sub New(fileName As String)
        If String.IsNullOrWhiteSpace(fileName) Then
            Throw New ArgumentNullException("The fileName cannot be null or an empty string")
        End If

        safeHandle = CreateFile(
            fileName, GENERIC_READ, FILE_SHARE_READ, IntPtr.Zero,
            OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero)

        ' Get file size.
        Size = GetFileSize(safeHandle, upperWord)
        If Size = INVALID_FILE_SIZE Then
            Size = -1
        ElseIf upperWord > 0 Then
            Size = (CLng(upperWord) << 32) + Size
        End If
    End Sub

    Public ReadOnly Property Size As Long

    Public Sub Dispose() _
              Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub

    Protected Overridable Sub Dispose(disposing As Boolean)
        If disposed Then Exit Sub

        ' Dispose of managed resources here.
        If disposing Then
            safeHandle.Dispose()
        End If

        ' Dispose of any unmanaged resources not wrapped in safe handles.

        disposed = True
    End Sub
End Class

使用安全控制碼來執行衍生類別的處置模式Implement the dispose pattern for a derived class with safe handles

下列範例將說明衍生類別 DisposableStreamResource2 的處置模式,該類別繼承自上述範例中顯示的 DisposableStreamResource 類別。The following example illustrates the dispose pattern for a derived class, DisposableStreamResource2, that inherits from the DisposableStreamResource class presented in the previous example. 這個類別會加入額外的方法 WriteFileInfo,並使用 SafeFileHandle 物件包裝可寫入檔案的控制代碼。The class adds an additional method, WriteFileInfo, and uses a SafeFileHandle object to wrap the handle of the writable file.

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

public class DisposableStreamResource2 : DisposableStreamResource
{
    // Define additional constants.
    protected const uint GENERIC_WRITE = 0x40000000;
    protected const uint OPEN_ALWAYS = 4;

    // Define additional APIs.
    [DllImport("kernel32.dll")]
    protected static extern bool WriteFile(
        SafeFileHandle safeHandle, string lpBuffer,
        int nNumberOfBytesToWrite, out int lpNumberOfBytesWritten,
        IntPtr lpOverlapped);

    // To detect redundant calls
    private bool _disposed = false;
    private bool _created = false;
    private SafeFileHandle _safeHandle;
    private readonly string _fileName;

    public DisposableStreamResource2(string fileName) : base(fileName) => _fileName = fileName;

    public void WriteFileInfo()
    {
        if (!_created)
        {
            _safeHandle = CreateFile(
                @".\FileInfo.txt", GENERIC_WRITE, 0, IntPtr.Zero,
                OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero);

            _created = true;
        }

        string output = $"{_fileName}: {Size:N0} bytes\n";
        _ = WriteFile(_safeHandle, output, output.Length, out _, IntPtr.Zero);
    }

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

        // Release any managed resources here.
        if (disposing)
        {
            // Dispose managed state (managed objects).
            _safeHandle?.Dispose();
        }

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

        _disposed = true;

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

Public Class DisposableStreamResource2 : Inherits DisposableStreamResource
    ' Define additional constants.
    Protected Const GENERIC_WRITE As Integer = &H40000000
    Protected Const OPEN_ALWAYS As Integer = 4

    ' Define additional APIs.
    Protected Declare Function WriteFile Lib "kernel32.dll" (
        safeHandle As SafeFileHandle, lpBuffer As String,
        nNumberOfBytesToWrite As Integer, ByRef lpNumberOfBytesWritten As Integer,
        lpOverlapped As Object) As Boolean

    ' Define locals.
    Private disposed As Boolean = False
    Private created As Boolean = False
    Private safeHandle As SafeFileHandle
    Private ReadOnly filename As String

    Public Sub New(filename As String)
        MyBase.New(filename)
        Me.filename = filename
    End Sub

    Public Sub WriteFileInfo()
        If Not created Then
            safeHandle = CreateFile(
                ".\FileInfo.txt", GENERIC_WRITE, 0, IntPtr.Zero,
                OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, IntPtr.Zero)
            created = True
        End If

        Dim output As String = $"{filename }: {Size:N0} bytes {vbCrLf }"
        Dim result = WriteFile(safeHandle, output, output.Length, 0&, Nothing)
    End Sub

    Protected Overridable Overloads Sub Dispose(disposing As Boolean)
        If disposed Then Exit Sub

        ' Release any managed resources here.
        If disposing Then
            safeHandle?.Dispose()
        End If

        disposed = True
        ' Release any unmanaged resources not wrapped by safe handles here.

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

請參閱See also