次の方法で共有


System.IDisposable インターフェイス

この記事では、この API のリファレンス ドキュメントに補足的な解説を提供します。

IDisposable インターフェイスの主な用途は、アンマネージ リソースを解放することです。 ガベージ コレクターは、そのオブジェクトが使用されなくなったときに、マネージド オブジェクトに割り当てられたメモリを自動的に解放します。 ただし、ガベージ コレクションがいつ発生するかを予測することはできません。 さらに、ガベージ コレクターには、ウィンドウ ハンドルや開いているファイルやストリームなどのアンマネージ リソースに関する知識がありません。

このインターフェイスの Dispose メソッドを使用して、ガベージ コレクターと組み合わせてアンマネージ リソースを明示的に解放します。 オブジェクトのコンシューマーは、オブジェクトが不要になったときにこのメソッドを呼び出すことができます。

警告

既存のクラスに IDisposable インターフェイスを追加するのは破壊的変更です。 型の既存のコンシューマーは Disposeを呼び出すことができないので、型によって保持されているアンマネージ リソースが解放されることを確認することはできません。

IDisposable.Dispose実装は、インスタンスが所有するリソースが不要になったときに型のコンシューマーによって呼び出されるため、マネージド オブジェクトをSafeHandleでラップするか (推奨される代替方法)、コンシューマーがObject.Finalizeを呼び出し忘れた場合にアンマネージ リソースを解放するためにDisposeをオーバーライドする必要があります。

重要

.NET Framework では、C++ コンパイラはリソースの確定的な破棄をサポートしており、 Dispose メソッドを直接実装することはできません。

このインターフェイスと Object.Finalize メソッドの使用方法の詳細については、 ガベージ コレクションDispose メソッドの実装 に関するトピックを参照してください。

IDisposable を実装するオブジェクトを使用する

アプリで IDisposable インターフェイスを実装するオブジェクトを使用するだけの場合は、使用が完了したときにオブジェクトの IDisposable.Dispose 実装を呼び出す必要があります。 プログラミング言語に応じて、次の 2 つの方法のいずれかでこれを行うことができます。

  • C# および Visual Basic の using ステートメント、F# の use ステートメントまたは using 関数などの言語コンストラクトを使用します。
  • IDisposable.Disposetry/ ブロックでfinally実装への呼び出しをラップします。

IDisposable実装する型のドキュメントでは、その事実に注意し、そのDispose実装を呼び出すリマインダーが含まれています。

C#、F#、Visual Basic の using ステートメント

C# の using ステートメント、Visual Basic の Using ステートメント、F# の use ステートメントなどのコンストラクトが言語でサポートされている場合は、 IDisposable.Dispose を明示的に呼び出す代わりに使用できます。 次の例では、この方法を使用して、ファイルとその中の単語数に関する情報を保持する WordCount クラスを定義します。

using System;
using System.IO;
using System.Text.RegularExpressions;

public class WordCount
{
    private String filename = String.Empty;
    private int nWords = 0;
    private String pattern = @"\b\w+\b";

    public WordCount(string filename)
    {
        if (!File.Exists(filename))
            throw new FileNotFoundException("The file does not exist.");

        this.filename = filename;
        string txt = String.Empty;
        using (StreamReader sr = new StreamReader(filename))
        {
            txt = sr.ReadToEnd();
        }
        nWords = Regex.Matches(txt, pattern).Count;
    }

    public string FullName
    { get { return filename; } }

    public string Name
    { get { return Path.GetFileName(filename); } }

    public int Count
    { get { return nWords; } }
}
open System.IO
open System.Text.RegularExpressions

type WordCount(filename) =
    let txt = 
        if File.Exists filename |> not then
            raise (FileNotFoundException "The file does not exist.")

        use sr = new StreamReader(filename)
        sr.ReadToEnd()

    let pattern = @"\b\w+\b"
    
    let nWords = Regex.Matches(txt, pattern).Count

    member _.FullName = filename

    member _.Name = Path.GetFileName filename

    member _.Count = nWords
Imports System.IO
Imports System.Text.RegularExpressions

Public Class WordCount
   Private filename As String
   Private nWords As Integer
   Private pattern As String = "\b\w+\b" 

   Public Sub New(filename As String)
      If Not File.Exists(filename) Then
         Throw New FileNotFoundException("The file does not exist.")
      End If   
      
      Me.filename = filename
      Dim txt As String = String.Empty
      Using sr As New StreamReader(filename)
         txt = sr.ReadToEnd()
      End Using
      nWords = Regex.Matches(txt, pattern).Count
   End Sub
   
   Public ReadOnly Property FullName As String
      Get
         Return filename
      End Get   
   End Property
   
   Public ReadOnly Property Name As String
      Get
         Return Path.GetFileName(filename)
      End Get   
   End Property
   
   Public ReadOnly Property Count As Integer
      Get
         Return nWords
      End Get
   End Property
End Class

using ステートメント (F# のuse式) は、実際には構文上便利です。 コンパイル時に、言語コンパイラは、 try/finally ブロックの中間言語 (IL) を実装します。

usingステートメントの詳細については、「ステートメントの使用」または「ステートメント使用」のトピックを参照してください。

try/finally ブロック

プログラミング言語で、C# または Visual Basic の using ステートメントや F# の use ステートメントなどのコンストラクトがサポートされていない場合、または使用しない場合は、IDisposable.Disposefinallytry ステートメントの/ ブロックからfinally実装を呼び出すことができます。 次の例では、前の例の using ブロックを try/finally ブロックに置き換えます。

using System;
using System.IO;
using System.Text.RegularExpressions;

public class WordCount2
{
    private String filename = String.Empty;
    private int nWords = 0;
    private String pattern = @"\b\w+\b";

    public WordCount2(string filename)
    {
        if (!File.Exists(filename))
            throw new FileNotFoundException("The file does not exist.");

        this.filename = filename;
        string txt = String.Empty;
        StreamReader? sr = null;
        try
        {
            sr = new StreamReader(filename);
            txt = sr.ReadToEnd();
        }
        finally
        {
            if (sr != null) sr.Dispose();
        }
        nWords = Regex.Matches(txt, pattern).Count;
    }

    public string FullName
    { get { return filename; } }

    public string Name
    { get { return Path.GetFileName(filename); } }

    public int Count
    { get { return nWords; } }
}
open System.IO
open System.Text.RegularExpressions

type WordCount2(filename) =
    let txt = 
        if File.Exists filename |> not then
            raise (FileNotFoundException "The file does not exist.")

        let sr = new StreamReader(filename)
        try
            sr.ReadToEnd()
        finally
            sr.Dispose()

    let pattern = @"\b\w+\b"
    
    let nWords = Regex.Matches(txt, pattern).Count

    member _.FullName = filename

    member _.Name = Path.GetFileName filename

    member _.Count = nWords
Imports System.IO
Imports System.Text.RegularExpressions

Public Class WordCount2
   Private filename As String
   Private nWords As Integer
   Private pattern As String = "\b\w+\b" 

   Public Sub New(filename As String)
      If Not File.Exists(filename) Then
         Throw New FileNotFoundException("The file does not exist.")
      End If   
      
      Me.filename = filename
      Dim txt As String = String.Empty
      Dim sr As StreamReader = Nothing
      Try
         sr = New StreamReader(filename)
         txt = sr.ReadToEnd()
      Finally
         If sr IsNot Nothing Then sr.Dispose() 
      End Try
      nWords = Regex.Matches(txt, pattern).Count
   End Sub
   
   Public ReadOnly Property FullName As String
      Get
         Return filename
      End Get   
   End Property
   
   Public ReadOnly Property Name As String
      Get
         Return Path.GetFileName(filename)
      End Get   
   End Property
   
   Public ReadOnly Property Count As Integer
      Get
         Return nWords
      End Get
   End Property
End Class

try/finally パターンの詳細については、「Try...Catch...Finally ステートメント」、「try-finally」、「try...finally 式」、または「try-finally ステートメント」を参照してください。

IDisposable を実装する

型でアンマネージ リソースを直接使用する場合、または破棄可能なリソースを自分で使用する場合は、 IDisposable を実装する必要があります。 型のコンシューマーは、インスタンスが不要になったときにリソースを解放するために、 IDisposable.Dispose 実装を呼び出すことができます。 Disposeの呼び出しに失敗するケースを処理するには、SafeHandleから派生したクラスを使用してアンマネージ リソースをラップするか、参照型のObject.Finalize メソッドをオーバーライドする必要があります。 どちらの場合も、 Dispose メソッドを使用して、アンマネージド リソースの解放、解放、リセットなど、アンマネージ リソースを使用した後に必要なクリーンアップを実行します。 IDisposable.Disposeの実装の詳細については、Dispose(bool) メソッドのオーバーロードを参照してください

重要

アンマネージ リソースを使用する基底クラスを定義していて、破棄する必要があるサブクラスを含む、または持つ可能性がある場合は、次のセクションで説明するように、 IDisposable.Dispose メソッドを実装し、 Disposeの 2 番目のオーバーロードを提供する必要があります。

IDisposable と継承階層

破棄可能にする必要があるサブクラスを持つ基底クラスは、次のように IDisposable 実装する必要があります。 IDisposable (Visual Basic では sealed) ではない型に NotInheritable を実装する場合は、必ずこのパターンを使用してください。

  • 1 つのパブリックな非仮想 Dispose() メソッドと保護された仮想 Dispose(Boolean disposing) メソッドを提供する必要があります。
  • Dispose() メソッドは、Dispose(true)を呼び出す必要があり、パフォーマンスのために終了処理を抑制する必要があります。
  • 基本型はファイナライザーを含めることはできません。

次のコード フラグメントは、基底クラスの dispose パターンを反映しています。 型が Object.Finalize メソッドをオーバーライドしないことを前提としています。

using System;
using System.IO;
using System.Runtime.InteropServices;

class BaseClass1 : IDisposable
{
    // Flag: Has Dispose already been called?
    bool disposed = false;
    // Instantiate a FileStream instance.
    FileStream fs = new FileStream("test.txt", FileMode.OpenOrCreate);

    // Public implementation of Dispose pattern callable by consumers.
    public void Dispose()
    {
        Dispose(disposing: true);
        GC.SuppressFinalize(this);
    }

    // Protected implementation of Dispose pattern.
    protected virtual void Dispose(bool disposing)
    {
        if (disposed)
            return;

        if (disposing)
        {
            fs.Dispose();
            // Free any other managed objects here.
            //
        }

        disposed = true;
    }
}
open System
open System.IO

type BaseClass1() =
    // Flag: Has Dispose already been called?
    let mutable disposed = false

    // Instantiate a FileStream instance.
    let fs = new FileStream("test.txt", FileMode.OpenOrCreate)

    interface IDisposable with
        // Public implementation of Dispose pattern callable by consumers.
        member this.Dispose() =
            this.Dispose true
            GC.SuppressFinalize this

    // Implementation of Dispose pattern.
    abstract Dispose: bool -> unit
    override _.Dispose(disposing) =
        if not disposed then
            if disposing then
                fs.Dispose()
                // Free any other managed objects here.
            disposed <- true
Imports System.IO
Imports System.Runtime.InteropServices

Class BaseClass1 : Implements IDisposable
   ' Flag: Has Dispose already been called?
   Dim disposed As Boolean = False
   ' Instantiate a FileStream instance.
   Dim fs As FileStream = New FileStream("test.txt", FileMode.OpenOrCreate)

   ' Public implementation of Dispose pattern callable by consumers.
   Public Sub Dispose() _
              Implements IDisposable.Dispose
      Dispose(disposing:=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
         fs.Dispose()
         ' Free any other managed objects here.
         '
      End If

      disposed = True
   End Sub
End Class

Object.Finalize メソッドをオーバーライドする場合、クラスは次のパターンを実装する必要があります。

using System;

class BaseClass2 : IDisposable
{
    // Flag: Has Dispose already been called?
    bool disposed = false;

    // Public implementation of Dispose pattern callable by consumers.
    public void Dispose()
    {
        Dispose(disposing: true);
        GC.SuppressFinalize(this);
    }

    // Protected implementation of Dispose pattern.
    protected virtual void Dispose(bool disposing)
    {
        if (disposed)
            return;

        if (disposing)
        {
            // Free any other managed objects here.
            //
        }

        // Free any unmanaged objects here.
        //
        disposed = true;
    }

    ~BaseClass2()
    {
        Dispose(disposing: false);
    }
}
open System

type BaseClass2() =
    // Flag: Has Dispose already been called?
    let mutable disposed = false

    interface IDisposable with
        // Public implementation of Dispose pattern callable by consumers.
        member this.Dispose() =
            this.Dispose true
            GC.SuppressFinalize this

    // Implementation of Dispose pattern.
    abstract Dispose: bool -> unit
    override _.Dispose(disposing) =
        if not disposed then
            if disposing then
                // Free any other managed objects here.
                ()

            // Free any unmanaged objects here.
            disposed <- true

    override this.Finalize() =
        this.Dispose false
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(disposing:=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(disposing:=False)
   End Sub
End Class

サブクラスは、破棄可能なパターンを次のように実装する必要があります。

  • Dispose(Boolean)をオーバーライドし、基底クラスDispose(Boolean)実装を呼び出す必要があります。
  • 必要な場合にはファイナライザーを提供します。 ファイナライザーは Dispose(false)を呼び出す必要があります。

派生クラス自体は、 IDisposable インターフェイスを実装せず、パラメーターなしの Dispose メソッドを含まないことに注意してください。 基底クラス Dispose(Boolean) メソッドのみをオーバーライドします。

次のコード フラグメントは、派生クラスの dispose パターンを反映しています。 型が Object.Finalize メソッドをオーバーライドしないことを前提としています。

using System;
using System.IO;
using System.Runtime.InteropServices;

class MyDerivedClass : MyBaseClass
{
    // Flag: Has Dispose already been called?
    bool disposed = false;
    // Instantiate a FileStream instance.
    FileStream fs = new FileStream("test.txt", FileMode.OpenOrCreate);

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

        if (disposing)
        {
            fs.Dispose();
            // Free any other managed objects here.
            //
        }

        // Free any unmanaged objects here.
        //

        disposed = true;
        // Call base class implementation.
        base.Dispose(disposing);
    }
}
open Microsoft.Win32.SafeHandles
open System

type MyDerivedClass() =
    inherit MyBaseClass()
    
    // Flag: Has Dispose already been called?
    let mutable disposed = false
    // Instantiate a FileStream instance.
    let fs = new FileStream("test.txt", FileMode.OpenOrCreate)

    // Implementation of Dispose pattern.
    override _.Dispose(disposing) =
        if not disposed then
            if disposing then
                fs.Dispose()
                // Free any other managed objects here.

            // Free any unmanaged objects here.
            disposed <- true
            // Call base class implementation.
            base.Dispose disposing
Imports System.IO
Imports System.Runtime.InteropServices

Class DerivedClass2 : Inherits BaseClass2
   ' Flag: Has Dispose already been called?
   Dim disposed As Boolean = False
   ' Instantiate a FileStream instance.
   Dim fs As FileStream = New FileStream("test.txt", FileMode.OpenOrCreate)

   ' Protected implementation of Dispose pattern.
   Protected Overrides Sub Dispose(disposing As Boolean)
      If disposed Then Return

      If disposing Then
         fs.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