实现 Dispose 方法Implement a Dispose method

实现 Dispose 方法主要用于释放非托管资源。Implementing the Dispose method is primarily for releasing unmanaged resources. 处理 IDisposable 实现的实例成员时,通常会级联 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, for example, to free memory that was allocated, remove an item that was added to a collection, or signal the release of a lock that was acquired.

.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 是一种抽象托管类型,该类型包装了可标识非托管资源的 System.IntPtrA System.Runtime.InteropServices.SafeHandle is an abstract managed type that wraps an System.IntPtr that identifies an unmanaged resource. 在 Windows 上,它可能标识一个句柄,而在 Unix 上则可能标识一个文件描述符。On Windows it might identify a handle while on Unix, a file descriptor. 它提供了所有必要的逻辑,以确保在处理 SafeHandle 或删除对 SafeHandle 的所有引用并最终完成 SafeHandle 实例时,只释放该资源一次。It 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 会将其包装在 SafeHandle 中,并根据需要返回此 SafeHandle,而不是返回原始指针。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 类型需要实现终结器;大多数可释放模式实现最终只包装其他受管理资源,其中某些资源可能是 SafeHandleAs 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 非虚拟的(Visual Basic 中的 NonInheritableIDisposable.Dispose 实现。A public non-virtual (NonInheritable in Visual Basic) IDisposable.Dispose implementation that has no parameters.

  • protected virtual(Visual Basic 中为 OverridableDispose 方法,其签名为: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,从 IDisposable.Dispose 方法调用时应为 trueThe 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、非虚拟(Visual Basic 中为 NonInheritable)、无参数的 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. 释放与托管对象关联的实际内存始终是垃圾回收器的域。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:

  • 释放非托管资源的块。A block that frees unmanaged resources. 无论 disposing 参数的值如何,都会执行此块。This block executes regardless of the value of the disposing parameter.

  • 释放托管资源的条件块。A conditional block that frees managed resources. 如果 disposing 的值为 true,则执行此块。This block executes if the value of disposing is true. 它释放的托管资源可包括:The managed resources that it frees can include:

    • 实现 IDisposable 的托管对象。Managed objects that implement IDisposable. 可用于调用其 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 objects that consume large amounts of memory or consume scarce resources. 将大型托管对象引用分配到 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. 这一点很重要,因为垃圾回收器在终止期间销毁托管对象的顺序是不确定的。This is important because the order in which the garbage collector destroys managed objects during finalization is non-deterministic.

级联释放调用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. 这是为了帮助确保引用的可释放类型可通过 Dispose 方法明确执行清理。This is to help ensure that the referenced disposable types are given the opportunity to deterministically perform clean up through the Dispose method. 在本例中,类为 sealed(Visual Basic 中为 NotInheritable)。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

所有非密封类或(未修改为 NotInheritable 的 Visual Basic 类)都应被视为潜在的基类,因为它们可以被继承。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.
  • 从包装非托管资源的 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.

重要

基类可以只引用托管对象,并实现释放模式。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.Finalize 创建一个终结器In 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)(Visual Basic 中为 MyBase.Dispose(bool))方法,并传递参数的释放状态。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.
  • 从包装非托管资源的 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. 如果你提供了终结器,它必须调用 disposing 参数为 falseDispose(bool) 重载。If 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

下面的示例阐释了基类 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