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.IntPtr をラップする抽象マネージド型です。A 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. これが、このリソースが 1 回しか解放されないことを保証するために必要なすべてのロジックを提供するのは、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. たとえば、SafeFileHandleSafeHandle から派生し、開いているファイル ハンドル/記述子を識別する 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 以外の型でファイナライザーを実装する必要があるのはごく少数になります。ほとんどの破棄可能パターンの実装では、SafeHandle を含む他の管理対象リソースをラップするだけで終了します。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 インターフェイスでは、パラメーターのない Dispose メソッドを 1 つ実装する必要があります。The 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 では NonInheritable) の IDisposable.Dispose の実装。パラメーターはありません。A public non-virtual (NonInheritable in Visual Basic) IDisposable.Dispose implementation that has no parameters.

  • protected virtual (Visual Basic では Overridable) の 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 パラメーターは、ファイナライザーから呼び出されたときは falseIDisposable.Dispose メソッドから呼び出されたときは true にする必要があります。The disposing parameter should be false when called from a finalizer, and true when called from the IDisposable.Dispose method. つまり、確定的に呼び出されたときは true、非確定的に呼び出されたときは false です。In 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).

メソッドの本体は 2 つのコード ブロックで構成されます。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, and this is usually done outside of the conditional block.

メソッドの呼び出し元がファイナライザーの場合、アンマネージ リソースを解放するコードだけを実行する必要があります。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 を実装する場合、それを含むクラスそのものが IDisposable も実装する必要があります。If 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.

セーフ ハンドルを使用して基底クラスで Dispose パターンを実装する一般的なパターンを次に示します。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 をオーバーライドして基底クラスで Dispose パターンを実装する一般的なパターンを次に示します。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 引数を false として Dispose(bool) オーバーロードを呼び出す必要があります。If you do provide a finalizer, it must call the Dispose(bool) overload with a disposing argument of false.

セーフ ハンドルを使用して派生クラスで Dispose パターンを実装する一般的なパターンを次に示します。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 をオーバーライドして派生クラスで Dispose パターンを実装する一般的なパターンを次に示します。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 での Dispose パターンを示します。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 での Dispose パターンを示します。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