次の方法で共有


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

共通言語ランタイムのガベージ コレクター (GC) は、マネージド オブジェクトによって使用されるメモリを再利用します。 通常、アンマネージ リソースを使用する型は、 IDisposable または IAsyncDisposable インターフェイスを実装して、アンマネージ リソースを再利用できるようにします。 IDisposableを実装するオブジェクトの使用が完了したら、オブジェクトのDisposeを呼び出すか、DisposeAsync実装を呼び出して明示的にクリーンアップを実行します。 これは、次の 2 つの方法のいずれかで行うことができます。

  • C# の using ステートメントまたは宣言 (Visual Basic では Using) の使用します。
  • try/finally ブロックを実装し、DisposeDisposeAsync または finally メソッドを呼び出します。

Von Bedeutung

GC は、に関する知識がないため、オブジェクトを破棄IAsyncDisposable.DisposeAsync()。 GC は、オブジェクトがファイナライズ可能かどうか (つまり、 Object.Finalize() メソッドを定義する) と、オブジェクトのファイナライザーを呼び出す必要があるタイミングのみを認識します。 詳細については、「 最終処理のしくみ」を参照してください。 DisposeDisposeAsyncの実装の詳細については、次を参照してください。

明示的に指定しない限り、変数スコープに関係なく、 System.IDisposable または System.IAsyncDisposable を実装するオブジェクトは常に適切に破棄する必要があります。 アンマネージ リソースを解放するファイナライザーを定義する型は、通常、GC.SuppressFinalizeまたはDispose実装からDisposeAsyncを呼び出します。 SuppressFinalize の呼び出しは、ファイナライザーが既に実行されていて、終了処理のためにオブジェクトを昇格させてはならないことを、GC に示します。

using ステートメント

C# の using ステートメント と Visual Basic の Using ステートメント により、オブジェクトをクリーンアップするために記述する必要があるコードが簡略化されます。 using ステートメントは、1 つ以上のリソースを取得し、指定したステートメントを実行して、オブジェクトを自動的に破棄します。 ただし、 using ステートメントは、作成されるメソッドのスコープ内で使用されるオブジェクトにのみ役立ちます。

次の例では、 using ステートメントを使用して、 System.IO.StreamReader オブジェクトを作成および解放します。

using System.IO;

class UsingStatement
{
    static void Main()
    {
        var buffer = new char[50];
        using (StreamReader streamReader = new("file1.txt"))
        {
            int charsRead = 0;
            while (streamReader.Peek() != -1)
            {
                charsRead = streamReader.Read(buffer, 0, buffer.Length);
                //
                // Process characters read.
                //
            }
        }
    }
}
Imports System.IO

Module UsingStatement
    Public Sub Main()
        Dim buffer(49) As Char
        Using streamReader As New StreamReader("File1.txt")
            Dim charsRead As Integer
            Do While streamReader.Peek() <> -1
                charsRead = streamReader.Read(buffer, 0, buffer.Length)
                ' 
                ' Process characters read.
                '
            Loop
        End Using
    End Sub
End Module

using 宣言は、中かっこが削除され、スコープが暗黙的に決まる代替構文です。

using System.IO;

class UsingDeclaration
{
    static void Main()
    {
        var buffer = new char[50];
        using StreamReader streamReader = new("file1.txt");

        int charsRead = 0;
        while (streamReader.Peek() != -1)
        {
            charsRead = streamReader.Read(buffer, 0, buffer.Length);
            //
            // Process characters read.
            //
        }
    }
}

StreamReader クラスは、アンマネージ リソースを使用することを示すIDisposable インターフェイスを実装しますが、この例ではStreamReader.Dispose メソッドを明示的に呼び出しません。 C# または Visual Basic コンパイラは、 using ステートメントを検出すると、 try/finally ブロックを明示的に含む次のコードと同等の中間言語 (IL) を出力します。

using System.IO;

class TryFinallyGenerated
{
    static void Main()
    {
        var buffer = new char[50];
        StreamReader? streamReader = null;
        try
        {
            streamReader = new StreamReader("file1.txt");
            int charsRead = 0;
            while (streamReader.Peek() != -1)
            {
                charsRead = streamReader.Read(buffer, 0, buffer.Length);
                //
                // Process characters read.
                //
            }
        }
        finally
        {
            // If non-null, call the object's Dispose method.
            streamReader?.Dispose();
        }
    }
}
Imports System.IO

Module TryFinallyGenerated
    Public Sub Main()
        Dim buffer(49) As Char
        Dim streamReader As New StreamReader("File1.txt")
        Try
            Dim charsRead As Integer
            Do While streamReader.Peek() <> -1
                charsRead = streamReader.Read(buffer, 0, buffer.Length)
                ' 
                ' Process characters read.
                '
            Loop
        Finally
            If streamReader IsNot Nothing Then DirectCast(streamReader, IDisposable).Dispose()
        End Try
    End Sub
End Module

C# using ステートメントを使用すると、1 つのステートメントで複数のリソースを取得することもできます。これは、入れ子になった using ステートメントと内部的に同等です。 次の例では、2 つの StreamReader オブジェクトをインスタンス化して、2 つの異なるファイルの内容を読み取ります。

using System.IO;

class SingleStatementMultiple
{
    static void Main()
    {
        var buffer1 = new char[50];
        var buffer2 = new char[50];

        using StreamReader version1 = new("file1.txt"),
                           version2 = new("file2.txt");

        int charsRead1, charsRead2 = 0;
        while (version1.Peek() != -1 && version2.Peek() != -1)
        {
            charsRead1 = version1.Read(buffer1, 0, buffer1.Length);
            charsRead2 = version2.Read(buffer2, 0, buffer2.Length);
            //
            // Process characters read.
            //
        }
    }
}

Try/Finally ブロック

try/finally ステートメントでusing ブロックをラップする代わりに、try/finally ブロックを直接実装することもできます。 個人のコーディング スタイルである場合もあれば、次のいずれかの理由でこれを行う必要がある場合もあります。

  • catch ブロックでスローされた例外を処理する try ブロックを含めるため。 それ以外の場合、using ステートメント内でスローされた例外は処理されません。
  • スコープが宣言されているブロックに対してローカルではない IDisposable を実装するオブジェクトをインスタンス化します。

次の例は、 try/catch/finally ブロックを使用して StreamReader オブジェクトをインスタンス化、使用、破棄し、 StreamReader コンストラクターとその ReadToEnd メソッドによってスローされた例外を処理する点を除き、前の例と似ています。 finally ブロック内のコードは、IDisposable メソッドを呼び出す前に、nullを実装するオブジェクトがDisposeされていないことを確認します。 これを行わないと、実行時に NullReferenceException 例外が発生する可能性があります。

using System;
using System.Globalization;
using System.IO;

class TryExplicitCatchFinally
{
    static void Main()
    {
        StreamReader? streamReader = null;
        try
        {
            streamReader = new StreamReader("file1.txt");
            string contents = streamReader.ReadToEnd();
            var info = new StringInfo(contents);
            Console.WriteLine($"The file has {info.LengthInTextElements} text elements.");
        }
        catch (FileNotFoundException)
        {
            Console.WriteLine("The file cannot be found.");
        }
        catch (IOException)
        {
            Console.WriteLine("An I/O error has occurred.");
        }
        catch (OutOfMemoryException)
        {
            Console.WriteLine("There is insufficient memory to read the file.");
        }
        finally
        {
            streamReader?.Dispose();
        }
    }
}
Imports System.Globalization
Imports System.IO

Module TryExplicitCatchFinally
    Sub Main()
        Dim streamReader As StreamReader = Nothing
        Try
            streamReader = New StreamReader("file1.txt")
            Dim contents As String = streamReader.ReadToEnd()
            Dim info As StringInfo = New StringInfo(contents)
            Console.WriteLine($"The file has {info.LengthInTextElements} text elements.")
        Catch e As FileNotFoundException
            Console.WriteLine("The file cannot be found.")
        Catch e As IOException
            Console.WriteLine("An I/O error has occurred.")
        Catch e As OutOfMemoryException
            Console.WriteLine("There is insufficient memory to read the file.")
        Finally
            If streamReader IsNot Nothing Then streamReader.Dispose()
        End Try
    End Sub
End Module

プログラミング言語では try/finally ステートメントをサポートしていないが、using メソッドへの直接呼び出しが許可されるため、Dispose ブロックを実装するか、実装する必要がある場合は、この基本的なパターンに従うことができます。

IDisposable インスタンス メンバー

クラスがインスタンス フィールドまたはプロパティを所有し、その型が IDisposableを実装する場合、クラスは IDisposableも実装する必要があります。 詳細については、カスケード破棄の実装に関する記事を参照してください。

こちらも参照ください