共通言語ランタイムのガベージ コレクター (GC) は、マネージド オブジェクトによって使用されるメモリを再利用します。 通常、アンマネージ リソースを使用する型は、 IDisposable または IAsyncDisposable インターフェイスを実装して、アンマネージ リソースを再利用できるようにします。 IDisposableを実装するオブジェクトの使用が完了したら、オブジェクトのDisposeを呼び出すか、DisposeAsync実装を呼び出して明示的にクリーンアップを実行します。 これは、次の 2 つの方法のいずれかで行うことができます。
- C# の
using
ステートメントまたは宣言 (Visual Basic ではUsing
) の使用します。 -
try/finally
ブロックを実装し、Disposeで DisposeAsync またはfinally
メソッドを呼び出します。
Von Bedeutung
GC は、やに関する知識がないため、オブジェクトを破棄IAsyncDisposable.DisposeAsync()。 GC は、オブジェクトがファイナライズ可能かどうか (つまり、 Object.Finalize() メソッドを定義する) と、オブジェクトのファイナライザーを呼び出す必要があるタイミングのみを認識します。 詳細については、「 最終処理のしくみ」を参照してください。
Dispose
とDisposeAsync
の実装の詳細については、次を参照してください。
明示的に指定しない限り、変数スコープに関係なく、 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も実装する必要があります。 詳細については、カスケード破棄の実装に関する記事を参照してください。
こちらも参照ください
.NET