概要
デストラクターとは、オブジェクトがガベージ コレクションに回収されるときに呼び出される特別なメソッドです。
「リソースの破棄」で説明しているように、
基本的には、確保したリソースの後片付けはDispose
メソッドをusing
ステートメントを使って行います。
しかし、using
ステートメントは呼び忘れる可能性があって、100%保証のある後片付けにはなりません。
一方で、ガベージ コレクションによって回収されるタイミングであれば、
呼び忘れの心配はありません。
そのため、確実に解放しなければならないリソースは、Dispose
メソッドだけでなく、デストラクターでも後片付けを行います。
(もちろん、ガベージ コレクション自体を阻害するようなバグ(メモリ リーク)は起こり得て、
その場合はデストラクターも呼ばれないため、かなりまずいです。)
ポイント
~
+ クラス名で、デストラクターと呼ばれる特殊なメソッドが定義できます- デストラクターはオブジェクトがガベージ コレクションで回収される際に呼ばれます
- リソースの破棄を確実にするためには、
Dispose
メソッドに加えてデストラクターも定義します
デストラクター
コンストラクターとは逆に、インスタンスが破棄されるときに呼び出されるのがデストラクターです。
デストラクターは以下のように、クラス名の前に ~
を付けた名前のメソッドを書くことで定義できます。
class SampleClass
{
// ↓これがデストラクター
~SampleClass()
{
// インスタンスの破棄用のコードを書く
}
}
デストラクターはコンストラクターと違って、引数を持つことができません。
また、アクセシビリティも指定できず、static
にもできません。
注意: デストラクターの呼び出しタイミング
.NET Framework では、インスタンスの寿命は .NET Framework 自体が管理していて、 いつインスタンスの破棄が行われるのかは分かりません。 (C++ 言語に慣れている人は注意が必要。)
using System;
class Test
{
public Test()
{
Console.Write("Test クラスのコンストラクターが呼ばれました\n");
}
~Test()
{
Console.Write("Test クラスのデストラクターが呼ばれました\n");
}
}
class DestructorSample
{
static void Main()
{
Console.Write("1\n");
Test t = new Test(); // ここで Test のコンストラクターが呼ばれる
Console.Write("2\n");
t = null; // ↑で作成したインスタンスはもう利用されなくなる
// でも、デストラクターはまだ呼ばれない
Console.Write("3\n");
}
}
1 Test クラスのコンストラクターが呼ばれました 2 3 Test クラスのデストラクターが呼ばれました
この例では、デストラクターはプログラムの終了時に呼び出されます(「ガベージ コレクション」するときに呼ばれます)。 ガベージ コレクションのタイミングは、通常は制御できないので、デストラクターはいつ呼び出されるかわかりません。
このような性質を持っているため、通常、デストラクターはあまり利用されません。 ほぼ、非管理リソースの破棄漏れ防止用です(参考: 「IDisposable インターフェイスの実装」)。
破棄のタイミングを明示的に制御する必要がある場合 (例えば、何らかの外部リソース(ファイルやプリンタなど)の破棄(ファイルのバッファのフラッシュやプリンタの解放)を行う必要がある場合)、 後述する 「using ステートメント」というものを使った Dispose を行います。
注意: Finalize
デストラクターという呼び名と、~ 記号を使う構文は C++ の構文を参考にしたものです。 しかし、C++ のデストラクター(変数のスコープを抜けたとき、もしくは、delete 演算子を呼んだタイミングで呼ばれる)とは呼び出されるタイミングが全然違うので注意してください。
C++ では、特定のスコープを抜けた時に確実に呼びたい処理のためにデストラクターを使うことがありますが、 こういう用途には、C# の場合、 「using ステートメント」 を使います。
C# のデストラクターは、動作的にはむしろ、Java の finalize メソッド(ガベージ コレクションに回収された時点で呼ばれる)と同じです。 実際、C# では「デストラクター」と呼んでいるものは、.NET Framework の中間言語(= C#のコンパイル結果)的にはファイナライザーと呼びます。 呼び名の問題だけではなくて、中間言語にコンパイルした結果としては、デストラクターは Finalize という名前のメソッドになっています
ちなみに、通常、リソースの破棄処理は、 「using ステートメント」とデストラクターでの2段構えで行います。
using
ステートメントは高効率ですが確実性がなく、
デストラクターは確実ですがパフォーマンスが悪いです。
2段構えにすることで、忘れずusing
していればパフォーマンスがよく、忘れた場合でもデストラクターでの確実な破棄ができます。
詳しくは「IDisposable インターフェイスの実装」で説明しています。