class Example { ~Example() // デストラクタ { // 終了処理 } }
デストラクタの構文は、実際にはFinalize()の呼び出しとなります。
protected override void Finalize() // ファイナライザ { try { // 終了処理 } finally { base.Finalize(); } }
ファイナライザに頼らない方が良い理由。
よって終了処理が必須ならば、IDisposableインターフェイスを継承しDispose()を実装します。
class Example : System.IDisposable { private bool disposed = false; // Dispose(bool)がくり返し呼ばれた場合に対処するためのフラグ // デストラクタ // Dispose()が呼ばれなかった場合のため。解放するアンマネージ リソースがないならば不要 ~Example() { Dispose( false ); // アンマネージ リソースのみを解放する } public void Dispose() { Dispose( true ); // マネージとアンマネージ リソースを解放する // このインスタンスのファイナライザが呼び出されないようにする // ファイナライザ (デストラクタ) をオーバーライドしないならば不要 System.GC.SuppressFinalize( this ); } protected virtual void Dispose( bool disposing ) { if( !disposed ) { if( disposing ) { // マネージド リソースを解放する // 引数なしのDispose()から呼ばれたときに実行する処理であり、デストラクタから呼ばれたときは処理しない } // アンマネージド リソースを解放する disposed = true; } } }IDisposable インターフェイス (System) | Microsoft Learn GC.SuppressFinalize(Object) メソッド (System) | Microsoft Learn
ファイナライザを呼び出す前にCLRがシャットダウンされると、ファイナライザー スレッドが開始され、そこでファイナライザが呼ばれます。Remarks - Environment.HasShutdownStarted Property (System) | Microsoft Learn
明示的にリソースを解放する必要があるならば、IDisposableインターフェイスを実装します。そしてそのインターフェイスを実装しているクラスのオブジェクトは、Dispose()メソッドにより解放できます。IDisposable Interface (System) | Microsoft Learn
void Dispose()IDisposable.Dispose Method (System) | Microsoft Learn
IDisposableを実装しているオブジェクトがスコープ内で破棄されない可能性があることは、コード分析のCA2000で検出できます。
usingの対象とできる型は、IDisposableを継承したクラスだけです。Compiler Error CS1674 | MSDN
using (FileStream fs = new FileStream("sample.txt", FileMode.Create)) { // オブジェクトを使用した処理 }
usingステートメントはDispose()を確実に呼び出すための簡易構文に過ぎず、コンパイル時には次のように展開されます。
{
FileStream fs = new FileStream("sample.txt", FileMode.Create);
try
{
// オブジェクトを使用した処理
}
finally
{
if (fs != null)
{
((IDisposable)fs).Dispose();
}
}
}
ネストされたusing宣言は、
using (ResourceType r1 = e1) { using (ResourceType r2 = e2) { } }
次のようにも記述できます。
using (ResourceType r1 = e1) using (ResourceType r2 = e2) { }
このときインスタンスのクラスが同一ならば、カンマ区切りで並べて記述できます。
using (ResourceType r1 = e1, r2 = e2) { }
入れ子の内側のusingステートメントのリソースに、外側のusingステートメントのリソースを格納する形式となる場合には、usingでネストせずtry-finallyで記述するようにします。このようにネストすると同一のリソースがくり返し破棄される恐れがあるため、コード分析で「オブジェクトを複数回破棄しません」としてCA2202で警告されます。
C# 8.0以降ならば、usingステートメントの中かっこを省略して記述できます。using declaration - Pattern based using and using declarations - C# 8.0 draft feature specifications | Microsoft Learn
つまり、
{
using (FileStream f = new FileStream("sample.txt"))
{
// オブジェクトを使用した処理
}
}
のような記述は次のようにかっこを省略でき、オブジェクトの寿命はそれが宣言されているスコープの最後までとなります。
{
using FileStream f = new FileStream("sample.txt");
// オブジェクトを使用した処理
}
基本的にガベージ コレクタが自動で破棄するため、明示的な処理は不要です。しかし必要ならば、Dispose()を呼びます。
obj.Dispose();
もし対象のオブジェクトがIDisposableを実装しておらず、Dispose()を呼べないならば
obj = null;
とします。
コード分析の実行により、「'A' は、IDisposable 型 'B' であるフィールド 'A.B' を含んでいます。このフィールドで Dispose または Close を呼び出すには、'A' の Dispose メソッドを変更してください。」と警告されるときには、クラスAのDispose()内で、フィールドBのDispose()を呼び出すようにします。
この警告はIDisposableを継承したクラスが、同様にIDisposableを継承したクラスをフィールドに持つとき、Dispose()メソッドでフィールドのDispose()を呼び出していない場合に発生します。CA2213: 破棄可能なフィールドは破棄されなければなりません | MSDN
適切に解放されているか、ツールを用いて確認します。≫ プロファイリング ツール