Dispose メソッドの実装

Dispose パターンと呼ばれる、オブジェクトを破棄するパターンによって、オブジェクトの有効期間に順番が付けられます。 Dispose パターンは、アンマネージ リソースにアクセスするオブジェクトに対してのみ使用されます。 これは、使用されていないマネージ オブジェクトの解放にはガベージ コレクターが非常に有効であるためです。

任意の型の Dispose メソッドは、所有するすべてのリソースを解放する必要があります。 また、その型の基本型が所有しているすべてのリソースも、親の型の Dispose メソッドを呼び出して解放する必要があります。 親の型の Dispose メソッドは、所有するすべてのリソースを解放した後、それ自身の親の型の Dispose メソッドを呼び出す必要があります。この処理が、基本型の階層全体を通じて繰り返されます。 Dispose メソッドが複数回呼び出される場合でも、例外をスローすることなく呼び出されるようにして、リソースが常に適切にクリーンアップされるようにする必要があります。

マネージ リソース (配列など) のみを使用する型は、ガベージ コレクターによって自動的にクリアされるため、このような型で Dispose メソッドを実装しても、パフォーマンス上の利点はありません。 ネイティブ リソースを使用するマネージ オブジェクト、および .NET Framework に公開されている COM オブジェクトでは、主に Dispose メソッドを使用します。 ネイティブ リソース (FileStream クラスなど) を使用するマネージ オブジェクトは、IDisposable インターフェイスを実装しています。

重要 :重要

C++ プログラマはこのトピックを使用しないでください。代わりに、Destructors and Finalizers in Visual C++ を参照してください。.NET Framework Version 2.0 では、C++ コンパイラはリソースの確定的な破棄の実装をサポートし、Dispose メソッドを直接実装することは許可しません。

Dispose メソッドは、破棄するオブジェクトの SuppressFinalize メソッドを呼び出す必要があります。 SuppressFinalize を呼び出すと、オブジェクトが終了キューに置かれている場合は、そのオブジェクトの Finalize メソッドの呼び出しは行われません。 Finalize メソッドの実行は、パフォーマンスに影響を与えることを覚えておいてください。 Dispose メソッドがオブジェクトのクリーンアップを完了していれば、ガベージ コレクターがそのオブジェクトの Finalize メソッドを呼び出す必要はなくなります。

GC.KeepAlive メソッドのコード例では、再利用するオブジェクトのメンバーがまだ実行中の場合でも、ガベージ コレクションがファイナライザーを実行しようとします。 長い Dispose メソッドの最後に、KeepAlive メソッドを呼び出すことをお勧めします。

代替手段としての SafeHandle

オブジェクトのファイナライザーのコードを記述することは、正しく行わないと問題が発生する可能性がある複雑なタスクです。 そのため、Dispose パターンを実装するのではなく、SafeHandle オブジェクトを構築することをお勧めします。

SafeHandle クラスは、処理を中断することなくハンドルの割り当てと解放を行うことで、オブジェクトの有効期間に関する問題を単純化します。 これには、アプリケーション ドメインのアンロード中に確実に実行されるクリティカル ファイナライザーが含まれています。 セーフ ハンドルを使用する利点の詳細については、「セーフ ハンドルとクリティカル ファイナライズ」を参照してください。

System.Runtime.InteropServices 名前空間の SafeHandle クラスは、オペレーティング システム ハンドルの抽象ラッパー クラスです。 このクラスからの派生は困難です。 代わりに、次のセーフ ハンドルを提供する Microsoft.Win32.SafeHandles 名前空間の派生クラスを使用してください。

  • ファイルとパイプ

  • メモリ ビュー

  • 暗号の構成要素

  • レジストリ キー

  • 待機ハンドル

次のコード例は、Dispose メソッドをアンマネージ リソースをカプセル化したクラスに実装する推奨デザイン パターンを示しています。

リソース クラスは、通常はネイティブなクラスや API の組み合わせから派生されるため、その組み合わせに応じてカスタマイズする必要があります。 このコード パターンを元にしてリソース クラスを作成し、カプセル化するリソースに応じて必要なカスタマイズを行います。

Imports System
Imports System.IO
Class Program

    Public Shared Sub Main()
        Try
            ' Initialize a Stream resource to pass 
            ' to the DisposableResource class.
           Console.Write("Enter filename and its path: ")
            Dim fileSpec As String = Console.ReadLine
            Dim fs As FileStream = File.OpenRead(fileSpec)
            Dim TestObj As DisposableResource = New DisposableResource(fs)

            ' Use the resource.
            TestObj.DoSomethingWithResource()

            ' Dispose theresource.
            TestObj.Dispose()

        Catch e As FileNotFoundException
            Console.WriteLine(e.Message)
        End Try
    End Sub
End Class

' This class shows how to use a disposable resource.
' The resource is first initialized and passed to
' the constructor, but it could also be
' initialized in the constructor.
' The lifetime of the resource does not 
' exceed the lifetime of this instance.
' This type does not need a finalizer because it does not
' directly create a native resource like a file handle
' or memory in the unmanaged heap.
Public Class DisposableResource
    Implements IDisposable

    Private _resource As Stream

    Private _disposed As Boolean

    ' The stream passed to the constructor
    ' must be readable and not null.
    Public Sub New(ByVal stream As Stream)
        MyBase.New()
        If (stream Is Nothing) Then
            Throw New ArgumentNullException("Stream is null.")
        End If
        If Not stream.CanRead Then
            Throw New ArgumentException("Stream must be readable.")
        End If
        _resource = stream
        Dim objTypeName As String = _resource.GetType.ToString
        _disposed = False
    End Sub

    ' Demonstrates using the resource.
    ' It must not be already disposed.
    Public Sub DoSomethingWithResource()
        If _disposed Then
            Throw New ObjectDisposedException("Resource was disposed.")
        End If

        ' Show the number of bytes.
        Dim numBytes As Integer = CType(_resource.Length, Integer)
        Console.WriteLine("Number of bytes: {0}", numBytes.ToString)
    End Sub

    Public Overloads Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)

        ' Use SupressFinalize in case a subclass
        ' of this type implements a finalizer.
        GC.SuppressFinalize(Me)
    End Sub

    Protected Overridable Overloads Sub Dispose(ByVal disposing As Boolean)
        If Not _disposed Then

            ' If you need thread safety, use a lock around these 
            ' operations, as well as in your methods that use the resource.
            If disposing Then
                If (Not (_resource) Is Nothing) Then
                    _resource.Dispose()
                End If
                Console.WriteLine("Object disposed.")
            End If

            ' Indicates that the instance has been disposed.
            _resource = Nothing
            _disposed = True
        End If
    End Sub
End Class
using System;
using System.IO;

class Program
{

    static void Main()
    {
        try
        {
            // Initialize a Stream resource to pass 
            // to the DisposableResource class.
            Console.Write("Enter filename and its path: ");
            string fileSpec = Console.ReadLine();
            FileStream fs = File.OpenRead(fileSpec);
            DisposableResource TestObj = new DisposableResource(fs);

            // Use the resource.
            TestObj.DoSomethingWithResource();

            // Dispose the resource.
            TestObj.Dispose();

        }
        catch (FileNotFoundException e)
        {
            Console.WriteLine(e.Message);
        }
    }
}


// This class shows how to use a disposable resource.
// The resource is first initialized and passed to
// the constructor, but it could also be
// initialized in the constructor.
// The lifetime of the resource does not 
// exceed the lifetime of this instance.
// This type does not need a finalizer because it does not
// directly create a native resource like a file handle
// or memory in the unmanaged heap.

public class DisposableResource : IDisposable
{

    private Stream _resource;  
    private bool _disposed;

    // The stream passed to the constructor 
    // must be readable and not null.
    public DisposableResource(Stream stream)
    {
        if (stream == null)
            throw new ArgumentNullException("Stream in null.");
        if (!stream.CanRead)
            throw new ArgumentException("Stream must be readable.");

        _resource = stream;

        _disposed = false;
    }

    // Demonstrates using the resource. 
    // It must not be already disposed.
    public void DoSomethingWithResource() {
        if (_disposed)
            throw new ObjectDisposedException("Resource was disposed.");

        // Show the number of bytes.
        int numBytes = (int) _resource.Length;
        Console.WriteLine("Number of bytes: {0}", numBytes.ToString());
    }


    public void Dispose() 
    {
        Dispose(true);

        // Use SupressFinalize in case a subclass
        // of this type implements a finalizer.
        GC.SuppressFinalize(this);      
    }

    protected virtual void Dispose(bool disposing)
    {
        // If you need thread safety, use a lock around these 
        // operations, as well as in your methods that use the resource.
        if (!_disposed)
        {
            if (disposing) {
                if (_resource != null)
                    _resource.Dispose();
                    Console.WriteLine("Object disposed.");
            }

            // Indicate that the instance has been disposed.
            _resource = null;
            _disposed = true;   
        }
    }
}

参照

参照

SuppressFinalize

Destructors and Finalizers in Visual C++

アンマネージ リソースをクリーンアップするための Finalize および Dispose の実装

概念

Finalize メソッドのオーバーライド