IDisposable を実装するオブジェクトの使用
共通言語ランタイムのガベージ コレクター (GC) により、マネージド オブジェクトによって使用されているメモリが回収されます。 通常、アンマネージド リソースを使用する型では、アンマネージド リソースを回収できるように、IDisposable または IAsyncDisposable インターフェイスが実装されています。 IDisposable が実装されているオブジェクトを使い終わったら、オブジェクトの Dispose または DisposeAsync の実装を呼び出して、明示的にクリーンアップを実行します。 2 つの方法のいずれかでこれを行うことができます。
- C# の
using
ステートメントまたは宣言 (Visual Basic ではUsing
) の使用します。 try/finally
ブロックを実装し、finally
で Dispose または DisposeAsync メソッドを呼び出します。
重要
GC でユーザーのオブジェクトの破棄は "行われません"。これは、IDisposable.Dispose() または IAsyncDisposable.DisposeAsync() に関する知識がないためです。 GC が認識しているのは、オブジェクトがファイナライズ可能 (つまり、Object.Finalize() メソッドが定義されている) かどうかと、オブジェクトのファイナライザーを呼び出す必要があるタイミングだけです。 詳細については、「終了処理のしくみ」を参照してください。 Dispose
と DisposeAsync
の実装に関するその他の詳細については、以下を参照してください。
System.IDisposable または System.IAsyncDisposable が実装されているオブジェクトは、明示的に指定されている場合を除き、変数のスコープに関係なく、常に正しく破棄する必要があります。 アンマネージド リソースを解放するファイナライザーが定義されている型では、通常、Dispose
または DisposeAsync
の実装から GC.SuppressFinalize が呼び出されます。 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
ステートメントでは、単一のステートメントで複数のリソースを取得できます。そのようなステートメントは、内部的には複数の 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 ブロック
using
ステートメントで try/finally
ブロックをラップする代わりに、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
この基本パターンを利用できるのは、プログラミング言語で using
ステートメントがサポートされていないが、Dispose メソッドを直接呼び出すことはできるため、try/finally
ブロックの実装を選択した場合、または実装する必要がある場合です。
IDisposable インスタンス メンバー
クラスがインスタンス フィールドまたはプロパティを所有しており、その型が IDisposable を実装する場合、そのクラスも IDisposable を実装する必要があります。 詳細については、カスケード破棄の実装に関する記事を参照してください。
関連項目
.NET