Top > .NET Visual C#言語入門 > コンストラクタ・デストラクタ
プログラム構造 列挙型(enum) コンストラクタ・デストラクタ
スケルトン 配列 メソッド
クラス内の記述構成 構造体 プロパティ
HalloWorld 修飾子 静的クラスと静的メンバ
コマンドライン引数 プリプロセッサ ディレクティブ フィールド
コメント 例外と例外処理 演算子のオーバーロード
ステートメント(文) 可変個引数リスト(params) イベント(割り込み)
式(演算子) 名前空間 デリゲート
変数とデータ型 クラスとオブジェクト インデクサ
型変換(キャスト) クラス(class) デリゲート&イベント発生と受信サンプル
定数(Const/Readonly) 部分クラス定義  
文字列(string) 継承  

コンストラクタ・デストラクタ

コンストラクタ

クラスまたは構造体がインスタンス化(newで生成)されたとき、そのタイミングで必ず呼び出されるメソッドです。このメソッド内でオブジェクトの状態(変数など)を初期化することで、不確定な状態で実行することを防止することができます。静的クラスもコンストラクタを持つことができます。

引数の異なる、フック数のコンストラクタを定義することができます。

なお、コンストラクタが実装されていない場合、すべてのメンバ変数に対して既定値で初期化されます。

また、オブジェクトが破棄されるタイミングで、デストラクタメソッドがあれば呼び出されます。これは、オブジェクト内で使用中のリソースを解放する際に役立つ機能です。例えば、ファイルをOpenしたままのオブジェクトに対し、ファイルの更新が中途状態にならずにCloseすることができます。

なお、Formなどのオブジェクトを継承する場合、デストラクタではなく親クラスのDisposeメソッドが呼び出されます。デザイナでDisposeメソッドが自動生成(override)されていますので、必要な処理があれば追記します。デストラクタ・メソッドを定義しても、呼び出されませんので注意が必要です。

コンストラクタの使用

コンストラクタは、特定の型のオブジェクトを作成するときに実行されるクラスのメソッドです。コンストラクタはクラスと同じ名前を持ち、通常、新しいオブジェクトのデータ メンバを初期化します。

次の例では、Taxi というクラスを簡単なコンストラクタで定義しています。このクラスは、次に new 演算子によってインスタンス化されます。新しいオブジェクトにメモリが割り当てられるとすぐに、Taxi コンストラクタが new 演算子によって呼び出されます。

public class Taxi
{
public bool isInitialized;

public Taxi()
    {
        isInitialized = true;
    }
}

class TestTaxi
{
static void Main()
    {
        Taxi t = new Taxi();
        System.Console.WriteLine(t.isInitialized);
    }
}

パラメータを受け取らないコンストラクタを既定のコンストラクタと呼びます。既定のコンストラクタは、new 演算子を使用してオブジェクトをインスタンス化する際に new に引数が渡されない場合に呼び出されます。

クラスが静的である場合を除き、コンストラクタが存在しないクラスには、C# コンパイラによりパブリックな既定のコンストラクタが割り当てられ、クラスをインスタンス化できるようになります。詳細については、「静的クラスと静的クラス メンバ」を参照してください。

次のようにコンストラクタをプライベートにすると、クラスがインスタンス化されないようにできます。

class NLog
{
private NLog() { }
public static double e = System.Math.E;  //2.71828...
}

詳細については、「プライベート コンストラクタ」を参照してください。

struct 型のコンストラクタはクラス コンストラクタに似ていますが、structs には、既定のコンストラクタがコンパイラによって自動的に提供されるため、明示的な既定のコンストラクタを含めることができません。このコンストラクタは、構造体の各フィールドを、「既定値の一覧表 (C# リファレンス)」に掲載されている既定値に初期化します。ただし、この既定のコンストラクタは、構造体が new によってインスタンス化される場合にのみ呼び出されます。たとえば、次のコードでは、Int32 に対して既定のコンストラクタが使用されるため、確実に整数を初期化できます。

int i = new int();
Console.WriteLine(i);

しかし、次のコードでは、new を使用せず、さらに初期化されていないオブジェクトの使用を試みるため、コンパイラ エラー CS0165 が生成されます。

int i;
Console.WriteLine(i);

これに対して、structs に基づくオブジェクトは次のように初期化や代入ができるため、使用できます。

int a = 44;  // Initialize the value type...
int b;
b = 33;      // Or assign it before using it.
Console.WriteLine("{0}, {1}", a, b);

そのため、値型の既定のコンストラクタを呼び出す必要がありません。

クラスも structs も共に、パラメータを受け取るコンストラクタを定義できます。パラメータを受け取るコンストラクタは、new ステートメントまたは base ステートメントを使用して呼び出す必要があります。クラスと structs では複数のコンストラクタも定義でき、クラスと構造体のどちらも既定のコンストラクタを定義する必要はありません。次に例を示します。

public class Employee
{
public int salary;

public Employee(int annualSalary)
    {
        salary = annualSalary;
    }

public Employee(int weeklySalary, int numberOfWeeks)
    {
        salary = weeklySalary * numberOfWeeks;
    }
}

このクラスは、次のいずれかのステートメントを使用して作成できます。

Employee e1 = new Employee(30000);
Employee e2 = new Employee(500, 52);

コンストラクタでは、base キーワードを使用して、基本クラスのコンストラクタを呼び出すことができます。次に例を示します。

public class Manager : Employee
{
public Manager(int annualSalary)
        : base(annualSalary)
    {
//Add further instructions here.
}
}

この例では、コンストラクタのブロックを実行する前に基本クラスのコンストラクタを呼び出しています。base キーワードは、パラメータの有無に関係なく使用することもできます。コンストラクタのパラメータは、base のパラメータまたは、式の一部として使用できます。詳細については、「base」を参照してください。

派生クラスでは、base キーワードを使用して基本クラスのコンストラクタを明示的に呼び出さないと、既定のコンストラクタ (存在する場合) が暗黙的に呼び出されます。そのため、次に示すコンストラクタの宣言も実質的に同じです。

public Manager(int initialdata)
{
//Add further instructions here.
}

public Manager(int initialdata) : base()
{
//Add further instructions here.
}

基本クラスが既定のコンストラクタを提供しない場合、派生クラスでは、base を使って基本コンストラクタを明示的に呼び出す必要があります。

コンストラクタで this キーワードを使用すると、同じオブジェクトで別のコンストラクタを呼び出すことができます。base と同様に、this もパラメータの有無に関係なく使用でき、コンストラクタのパラメータはいずれも this のパラメータとしても、式の一部としても使用できます。たとえば、上の例の 2 番目のコンストラクタは、this を使用して次のように書き直すことができます。

public Employee(int weeklySalary, int numberOfWeeks)
    : this(weeklySalary * numberOfWeeks)
{
    //
}

上の this キーワードを使用すると、次のコンストラクタが呼び出されます。

public Employee(int annualSalary)
{
    salary = annualSalary;
}

コンストラクタは、public、private、protected、internal、または protected internal とマークできます。これらのアクセス修飾子により、クラスのユーザーがクラスを作成する方法が定義されます。詳細については、「アクセス修飾子」を参照してください。

コンストラクタは、static キーワードを使用して静的と宣言できます。静的コンストラクタは、静的フィールドがアクセスされる直前に自動的に呼び出され、一般に静的なクラス メンバを初期化するために使用されます。詳細については、「静的コンストラクタ」を参照してください。

インスタンス コンストラクタ

インスタンス コンストラクタは、インスタンスを作成および初期化するために使用します。次の例に示すように新しいオブジェクトを作成すると、クラス コンストラクタが呼び出されます。

class CoOrds
{
public int x, y;

public CoOrds()
    {
        x = 0;
        y = 0;
    }
}
メモ
このクラスには、パブリック データ メンバが含まれていますが、これはわかりやすくするためであり、推奨されるプログラミング手法ではありません。この場合、プログラム内のすべてのメソッドに、オブジェクトの内部処理への無制限で検証されないアクセスが許可されるからです。通常、データ メンバはプライベートにし、クラス メソッドとプロパティのみを介してアクセスする必要があります。

このコンストラクタは、CoOrds クラスからオブジェクトを作成するときに呼び出されます。引数を取らないこのようなコンストラクタを既定のコンストラクタと呼びます。これは、コンストラクタを追加する場合に便利です。たとえば、CoOrds クラスに、データ メンバの初期値を指定できるコンストラクタを追加できます。

public CoOrds(int x, int y)
{
this.x = x;
this.y = y;
}

これにより、次のように、既定値や特定の初期値を使って CoOrd オブジェクトを作成できます。

CoOrds p1 = new CoOrds();
CoOrds p2 = new CoOrds(5, 3);

クラスに既定のコンストラクタが存在しない場合は、コンストラクタが自動的に生成され、既定値を使ってオブジェクト フィールドが初期化されます (たとえば、int は 0 (ゼロ) に初期化されます)。既定値の詳細については、「既定値の一覧表 (C# リファレンス)」を参照してください。したがって、CoOrds クラスの既定のコンストラクタはすべてのデータ メンバをゼロに初期化するので、クラスの機能を変更せずに完全に削除できます。下の「例 1」は複数のコンストラクタを使用する完全な例で、「例 2」は自動的に生成されるコンストラクタの例を示しています。

また、インスタンス コンストラクタを使用すると、基本クラスのインスタンス コンストラクタを呼び出すこともできます。クラス コンストラクタは、初期化子を通じて基本クラスのコンストラクタを呼び出すことができます。次にその例を示します。

class Circle : Shape
{
public Circle(double radius)
        : base(radius, 0)
    {
    }
}

この例の Circle クラスは、Circle の派生元の Shape によって提供されるコンストラクタに、半径と高さを表す値を渡します。下の「例 3」は、ShapeCircle を使用する完全な例を示しています。

例 1

次の例は、クラス コンストラクタを 2 つ使用するクラスを示しています。1 つのコンストラクタには引数がなく、もう 1 つのコンストラクタには引数が 2 つあります。

class CoOrds
{
public int x, y;

public CoOrds()
    {
        x = 0;
        y = 0;
    }

public CoOrds(int x, int y)
    {
this.x = x;
this.y = y;
    }

public override string ToString()
    {
return (System.String.Format("({0},{1})", x, y));
    }
}

class MainClass
{
static void Main()
    {
        CoOrds p1 = new CoOrds();
        CoOrds p2 = new CoOrds(5, 3);

        System.Console.WriteLine("CoOrds #1 at {0}", p1);
        System.Console.WriteLine("CoOrds #2 at {0}", p2);
    }
}

出力

CoOrds #1 at (0,0)
CoOrds #2 at (5,3)

例 2

 次の例の Person クラスにはコンストラクタがありません。この場合は、既定のコンストラクタが自動的に使用され、フィールドは既定値に初期化されます。
public class Person
{
public int age;
public string name;
}

class TestPerson
{
static void Main()
    {
        Person p = new Person();

        System.Console.Write("Name: {0}, Age: {1}", p.name, p.age);
    }
}

出力

Name: , Age: 0
age
の既定値は 0name の既定値は null です。既定値の詳細については、「既定値の一覧表」を参照してください。

 

例 3

次の例は、基本クラスの初期化子の使い方を示しています。Circle クラスは一般的なクラス Shape から派生しており、Cylinder クラスは Circle クラスから派生しています。どちらの派生クラスのコンストラクタも、基本クラスの初期化子を使用しています。

abstract class Shape
{
public const double pi = System.Math.PI;
protected double x, y;

public Shape(double x, double y)
    {
this.x = x;
this.y = y;
    }

public abstract double Area();
}

class Circle : Shape
{
public Circle(double radius)
        : base(radius, 0)
    {
    }
public override double Area()
    {
return pi * x * x;
    }
}

class Cylinder : Circle
{
public Cylinder(double radius, double height)
        : base(radius)
    {
        y = height;
    }

public override double Area()
    {
return (2 * base.Area()) + (2 * pi * x * y);
    }
}

class TestShapes
{
static void Main()
    {
        double radius = 2.5;
        double height = 3.0;

        Circle ring = new Circle(radius);
        Cylinder tube = new Cylinder(radius, height);

        System.Console.WriteLine("Area of the circle = {0:F2}", ring.Area());
        System.Console.WriteLine("Area of the cylinder = {0:F2}", tube.Area());
    }
}

出力

Area of the circle = 19.63
Area of the cylinder = 86.39

基本クラスのコンストラクタを呼び出すその他の例については、「virtual」、「override」、および「base」を参照してください。

プライベート・コンストラクタ

プライベート コンストラクタは、特別なインスタンス コンストラクタです。通常は、静的メンバだけを含むクラスで使用されます。クラスに 1 つ以上のプライベート コンストラクタがあり、パブリック コンストラクタがない場合、他のクラス (入れ子になったクラスを除く) はこのクラスのインスタンスを作成できません。以下にサンプルを示します。

class NLog
{
private NLog() { }
public static double e = System.Math.E;  //2.71828...
}

空のコンストラクタを宣言すると、既定コンストラクタの自動生成は行われません。コンストラクタにアクセス修飾子を指定しない場合でも、コンストラクタは既定でプライベートになります。しかし、通常は、private 修飾子を明示的に使って、クラスをインスタンス化できないことを明確に示します。

プライベート コンストラクタは、Math クラスなどのようにインスタンス フィールドやメソッドが存在しない場合や、クラスのインスタンスを取得するためにメソッドが呼び出される場合に、クラスのインスタンスが作成されないようにするために使用します。クラス内のすべてのメソッドが静的な場合は、クラス全体を静的にすることを検討してください。詳細については、「静的クラスと静的クラス メンバ」を参照してください。

使用例

プライベート コンストラクタを使用するクラスの例を次に示します。

public class Counter
{
private Counter() { }
public static int currentCount;
public static int IncrementCount()
    {
return ++currentCount;
    }
}

class TestCounter
{
static void Main()
    {
        Counter.currentCount = 100;
        Counter.IncrementCount();
        System.Console.WriteLine("New count: {0}", Counter.currentCount);
    }
}

出力

New count: 101
 この例で次のステートメントのコメントを解除すると、保護レベルが原因でコンストラクタにアクセスできなくなり、エラーが発生します。

// Counter aCounter = new Counter();   // Error

C# および C++ のデストラクタ構文 

C# プログラミング言語または C++ プログラミング言語からは、Object.Finalize メソッドを呼び出したり、オーバーライドしたりすることはできません。C# では、デストラクタを使用して終了コードを記述します。詳細については、「デストラクタ」を参照してください。

C++ では、デストラクタ構文は Dispose メソッドを実装する目的で使用されます。C++ には、Finalize メソッドを実装するための独自の構文が用意されています。詳細については、「Destructors and Finalizers in Visual C++」を参照してください。

メモ
.NET Framework Version 2.0 より前のバージョンでは、C++ でも C# と同じように Finalize メソッドを実装するのにデストラクタ構文を使用しました。Dispose メソッドを実装するための特別な構文はありませんでした。以前の構文を使用する C++ コードをコンパイルする場合は、/clr:oldSyntax コンパイラ オプションを使用します。

外観は似ていますが、C# デストラクタと C++ デストラクタには、アンマネージ C++ デストラクタと同じセマンティクスはありません。マネージ コードでは、C++ のデストラクタと類似のセマンティクスは一切サポートされていません。

デストラクタ

デストラクタは、クラスのインスタンスを消滅させるために使用します。

解説

  • デストラクタは、構造体には定義できません。クラスでだけ使用します。
  • クラスで使用できるデストラクタは 1 つだけです。
  • デストラクタを継承またはオーバーロードすることはできません。
  • デストラクタを呼び出すことはできません。デストラクタは自動的に起動されます。
  • デストラクタは修飾子をとらず、パラメータはありません。

次に示すのは、Car クラスに対するデストラクタの宣言の例です。

class Car
{
    ~ Car()  // destructor
{
// cleanup statements...
}
}

デストラクタは、オブジェクトの基本クラスで Finalize を暗黙的に呼び出します。したがって、前記のデストラクタのコードは、暗黙的に次のように解釈されます。

protected override void Finalize()
{
    try
    {
        // cleanup statements...
    }
    finally
    {
        base.Finalize();
    }
}

つまり、最派生クラスから最低派生クラスまで、継承チェーンのすべてのインスタンスに対して、Finalize メソッドが再帰的に呼び出されます。

メモ
空のデストラクタは使用しないでください。デストラクタがクラスに存在するときは、エントリが Finalize キューで作成されます。デストラクタを呼び出すと、ガベージ コレクタが呼び出され、このキューを処理します。デストラクタが空の場合は、これによってパフォーマンスが余計に低下します。

デストラクタがいつ呼び出されるかはガベージ コレクタによって決定されるため、プログラマは制御できません。ガベージ コレクタは、アプリケーションが使用していないオブジェクトをチェックします。消滅できるオブジェクトと考えられる場合、デストラクタ (存在する場合) を呼び出し、オブジェクトの格納に使用されているメモリをクリアします。デストラクタは、プログラムの終了時にも呼び出されます。

を呼び出すことによって、ガベージ コレクションを強制的に行うことができます。ただし、パフォーマンスに問題が発生する可能性があるため、通常はこの処理を避けます。詳細については、「ガベージ コレクションの強制実行」を参照してください。

デストラクタを使ったリソースの解放

一般に C# では、ガベージ コレクションを使用しない言語で開発する場合ほど、メモリ管理を必要としません。.NET Framework のガベージ コレクタが、オブジェクトに対するメモリの割り当てと解放を暗黙的に管理します。ただし、ウィンドウ、ファイル、ネットワーク接続などのアンマネージ リソースをアプリケーションでカプセル化するときは、デストラクタを使ってこれらのリソースを解放する必要があります。オブジェクトを消滅させることができる場合、ガベージ コレクタはそのオブジェクトの Finalize メソッドを実行します。

リソースの明示的な解放

 アプリケーションで貴重な外部リソースを使用している場合は、ガベージ コレクタがオブジェクトを解放する前にリソースを明示的に解放する手段を用意することをお勧めします。この処理を行うには、オブジェクトに対して必要なクリーンアップを実行する Dispose メソッドを IDisposableインターフェイスから実装します。これによって、アプリケーションのパフォーマンスを大幅に向上させることができます。このようにリソースを明示的に制御する場合でも、デストラクタは、Dispose メソッドの呼び出しが失敗したときにリソースをクリーンアップするための安全装置になります。

リソースのクリーンアップの詳細については、次のトピックを参照してください。

  • アンマネージ リソースのクリーンアップ
  • Dispose メソッドの実装
  • using ステートメント (C# リファレンス)

使用例

次の例では、継承のチェインを形成する 3 つのクラスを作成します。First が基本クラスであり、SecondFirst から派生し、ThirdSecond から派生しています。3 つのクラスのいずれにもデストラクタがあります。Main() では、最派生クラスのインスタンスが作成されます。プログラムを実行すると、3 つのクラスのデストラクタが、最派生クラスから最低派生クラスの順に自動的に呼び出されます。

class First
{
    ~First()
    {
        System.Console.WriteLine("First's destructor is called");
    }
}

class Second: First
{
    ~Second()
    {
        System.Console.WriteLine("Second's destructor is called");
    }
}

class Third: Second
{
    ~Third()
    {
        System.Console.WriteLine("Third's destructor is called");
    }
}

class TestDestructors
{
static void Main() 
    {
        Third t = new Third();
    }
}

出力

Third's destructor is called
Second's destructor is called
First's destructor is called

アンマネージ リソースのクリーンアップ

 Finalize メソッドのスコープを protected に制限して、アプリケーションのユーザーがオブジェクトの Finalize メソッドを直接呼び出すことができないようにしておく必要があります。また、アプリケーションのコードから基本クラス以外のクラスの Finalize メソッドを直接呼び出さないでください。アンマネージ リソースを正しく破棄するには、そのオブジェクトのクリーンアップ コードを実行するパブリックな Dispose メソッドまたは Close メソッドを実装することをお勧めします。IDisposable インターフェイスには、このインターフェイスを実装するリソース クラスのための Dispose メソッドが用意されています。Dispose メソッドはパブリック メソッドなので、アプリケーションのユーザーはこのメソッドを直接呼び出して、アンマネージ リソースで使用しているメモリを解放できます。Dispose メソッドを適切に実装すると、Finalize メソッドは、Dispose メソッドが呼び出されなかった場合にリソースをクリーンアップするための安全装置になります。正しい実装の詳細については、「Dispose メソッドの実装」を参照してください。

Disposeメソッドの実装

任意の型の Dispose メソッドは、所有するすべてのリソースを解放する必要があります。 また、その型の基本型が所有しているすべてのリソースも、親の型の Dispose メソッドを呼び出して解放する必要があります。親の型の Dispose メソッドは、所有するすべてのリソースを解放した後、それ自身の親の型の Dispose メソッドを呼び出す必要があります。この処理が、基本型の階層全体を通じて繰り返されます。Dispose メソッドが複数回呼び出される場合でも、例外をスローすることなく呼び出されるようにして、リソースが常に適切にクリーンアップされるようにする必要があります。

重要
C++ プログラマはこのトピックを使用しないでください。代わりに、Destructors and Finalizers in Visual C++ を参照してください。.NET Framework Version 2.0 では、C++ コンパイラはリソースの確定的な破棄の実装をサポートし、Dispose メソッドを直接実装することは許可しません。

 Dispose メソッドは、破棄するオブジェクトの GC.SuppressFinalize メソッドを呼び出す必要があります。GC.SuppressFinalize を呼び出すと、オブジェクトが終了キューに置かれている場合は、そのオブジェクトの Finalize メソッドの呼び出しは行われません。Finalize メソッドの実行は、パフォーマンスに影響を与えることを覚えておいてください。Dispose メソッドがオブジェクトのクリーンアップを完了していれば、ガベージ コレクタがそのオブジェクトの Finalize メソッドを呼び出す必要はなくなります。

メモ
System.GC.KeepAlive(System.Object) メソッドのコード例では、再利用するオブジェクトのメンバがまだ実行中の場合でも、ガベージ コレクションがファイナライザを実行しようとします。長い Dispose メソッドの最後に、KeepAlive メソッドを呼び出すことをお勧めします。

次のコード例は、Dispose メソッドをアンマネージ リソースをカプセル化したクラスに実装する推奨デザイン パターンを示すことを目的としています。このパターンは、.NET Framework 全体で実装されます。

リソース クラスは、通常はネイティブなクラスや API の組み合わせから派生されるため、その組み合わせに応じてカスタマイズする必要があります。このコード パターンを元にしてリソース クラスを作成し、カプセル化するリソースに応じて必要なカスタマイズを行います。このサンプルは、コンパイルしたり、アプリケーションに直接組み込んで使用したりすることはできません。

この例では、基本クラスの BaseResource は、クラスのユーザーが呼び出すことのできるパブリックな Dispose メソッドを実装します。このメソッドは、virtual Dispose(bool disposing) メソッド (Visual Basic では virtual Dispose(disposing As Boolean) メソッド) を呼び出します。呼び出し元の ID に応じて、true または false が渡されます。オブジェクトに対する適切なクリーンアップ コードは、仮想 Dispose メソッドで実行されます。

Dispose(bool disposing) メソッドは、異なる 2 種類のシナリオで実行されます。disposing が true の場合、このメソッドはユーザーのコードによって直接または間接に呼び出され、マネージ リソースとアンマネージ リソースの両方を破棄できます。disposing が false の場合、このメソッドは共通言語ランタイムによってファイナライザの内部から呼び出され、アンマネージ リソースだけを破棄できます。ファイナライザは特定の順序で実行されないため、オブジェクトがその終了コードを実行しているときには、他のオブジェクトを参照しないようにする必要があります。実行中のファイナライザが、既に終了されている別のオブジェクトを参照した場合、そのファイナライザは失敗します。

基本クラスでは、Dispose が呼び出されなかった場合の備えとして、Finalize メソッドまたはデストラクタが提供されています。Finalize メソッドは、複数のパラメータを受け取る Dispose メソッドを呼び出し、false を渡します。Finalize メソッド内で Dispose クリーンアップ コードを再作成しないでください。読みやすく保守のしやすいコードを作成するには、Dispose(false) の呼び出しが最も適しています。

MyResourceWrapper クラスでは、Dispose を使用したリソース管理を実装したクラスから派生する方法を示します。MyResourceWrappervirtual Dispose(bool disposing) メソッドをオーバーライドし、このクラス自体が作成したマネージ リソースおよびアンマネージ リソースのクリーンアップ コードを提供します。また、MyResourceWrapper はその基本クラスである BaseResourceDispose も呼び出し、基本クラスでも正しくクリーンアップを実行できるようにします。派生された MyResourceWrapper クラスは、Finalize メソッドや Dispose メソッドを基本クラスである BaseResource から継承するため、パラメータのない Finalize メソッドや Dispose メソッドを持ちません。

メモ
この例の protected Dispose(bool disposing) メソッドはユーザー スレッドとファイナライザ スレッドから同時に呼び出すことができないため、スレッド セーフは実現されていません。また、BaseResource を使用するクライアント アプリケーションでは、複数のユーザー スレッドが同時に protected Dispose(bool disposing) メソッドを呼び出すことを許可しないでください。アプリケーションやクラス ライブラリは、1 つのスレッドだけがリソースの有効期間を所有し、リソースが不要になったときに Dispose を呼び出すことを許可するようにデザインする必要があります。リソースによっては、リソースを破棄するときの非同期スレッド アクセスにより、セキュリティ上のリスクが伴う場合があります。開発者はコードを入念に調べて、スレッド セーフを実現するための最善の方法を決定する必要があります。
public class BaseResource: IDisposable
{
private IntPtr handle;
private Component Components;
private bool disposed = false;

public BaseResource()
   {
// クラスの初期化コードを記述します
}

public void Dispose()
   {
      Dispose(true);
      GC.SuppressFinalize(this);
   }

 protected virtual void Dispose(bool disposing)
   {
if(!this.disposed)
      {
if(disposing)
         {
            Components.Dispose();
         }

         CloseHandle(handle);
         handle = IntPtr.Zero;
      }
      disposed = true;         
   }

   ~BaseResource()      
   {
      Dispose(false);
   }

public void DoSomething()
   {
if(this.disposed)
      {
         throw new ObjectDisposedException();
      }
   }
}

public class MyResourceWrapper: BaseResource
{
private ManagedResource addedManaged;
private NativeResource addedNative;
private bool disposed = false;

public MyResourceWrapper()
   {
// クラスの初期化コードを記述します
}

protected override void Dispose(bool disposing)
   {
if(!this.disposed)
      {
try
{
if(disposing)
            {
               addedManaged.Dispose();         
            }
            CloseHandle(addedNative);
this.disposed = true;
         }
finally
{
base.Dispose(disposing);
         }
      }
   }
}

Close メソッドの実装

 Dispose メソッドを呼び出すよりも Close メソッドを呼び出す方が適切な型の場合は、その基本型にパブリックな Close メソッドを追加します。この Close メソッドは、パラメータを指定せずに Dispose メソッドを呼び出します。この Dispose メソッドは適切なクリーンアップ操作を実行します。Close メソッドのコード例を次に示します。

public void Close()
{
   Dispose();
}

Finalize メソッドのオーバーライド 

Finalize メソッドは、Dispose メソッドが呼び出されなかった場合にリソースをクリーンアップするための安全装置として動作します。Finalize メソッドは、アンマネージ リソースをクリーンアップするためにだけ実装するようにしてください。マネージ リソースのクリーンアップはガベージ コレクタが自動的に行うので、Finalize メソッドはマネージ オブジェクトに対しては実装しないでください。既定では、Object.Finalize メソッドは何も実行しません。ガベージ コレクタが、オブジェクトのメモリを再利用する前にオブジェクトをクリーンアップするようにするには、クラス内でこのメソッドをオーバーライドする必要があります。

メモ
C# プログラミング言語または C++ プログラミング言語で Finalize メソッドをオーバーライドすることはできません。C# では、デストラクタ構文を使用して、Finalize メソッドを実装します。.NET Framework Version 2.0 の C++ には Finalize メソッドを実装するための独自の構文が用意されています。詳細については、「Destructors and Finalizers in Visual C++」を参照してください。それ以前のバージョンでは C++ でも、C# と同じように、Finalize メソッドに対してデストラクタ構文が使用されていました。

Object.Finalize メソッドのスコープは "protected" です。クラス内でこのメソッドをオーバーライドする場合は、このスコープの範囲を維持する必要があります。Finalize メソッドを protected にしておくことで、アプリケーションのユーザーがオブジェクトの Finalize メソッドを直接呼び出すことを防ぐことができます。

オブジェクトの Finalize メソッドでは、そのオブジェクトが保持しているすべてのリソースを解放する必要があります。また、そのオブジェクトの基本クラスの Finalize メソッドを呼び出す必要もあります。オブジェクトの Finalize メソッドでは、基本クラス以外のオブジェクトのメソッドを呼び出さないでください。これは、共通言語ランタイムがシャットダウンされる場合など、呼び出し元のオブジェクトと呼び出された別のオブジェクトが同時にガベージ コレクションされることがあるからです。

Finalize メソッドを抜け出すためになんらかの例外を許容すると、システムではこのメソッドが返されたものと想定し、他のオブジェクトの Finalize メソッドの呼び出しを継続します。

▲ページトップに戻る

【本を読んでもよくらからない】 … 個別指導でわかりやすくお教えします