チュートリアル : Visual C# によるコンポーネントの作成
コンポーネントは、再利用できるコードをオブジェクトの形式で提供します。オブジェクトを作成し、オブジェクトのプロパティやメソッドを呼び出すことによってコンポーネントのコードを使用するアプリケーションは、クライアントと呼ばれます。クライアントは、使用するコンポーネントと同じアセンブリに含まれている場合もあれば、別の場所に存在する場合もあります。
次の手順は互いに関連するため、実行順序が重要です。
[!メモ]
実際に画面に表示されるダイアログ ボックスとメニュー コマンドは、アクティブな設定またはエディションによっては、ヘルプの説明と異なる場合があります。設定を変更するには、[ツール] メニューの [設定のインポートとエクスポート] をクリックします。詳細については、「Visual Studio での開発設定のカスタマイズ」を参照してください。
プロジェクトの作成
CDemoLib クラス ライブラリと CDemo コンポーネントを作成するには
[ファイル] メニューの [新規作成] をクリックし、[プロジェクト] をクリックして [新しいプロジェクト] ダイアログ ボックスを開きます。Visual C# プロジェクトの種類の一覧の [クラス ライブラリ] プロジェクト テンプレートを選択し、[プロジェクト名] ボックスに「CDemoLib」と入力します。
[!メモ]
新しいプロジェクトを作成するときには、必ずプロジェクトの名前を指定します。これにより、ルート名前空間、アセンブリ名、およびプロジェクト名が設定されます。また、既定のコンポーネントが正しい名前空間に含まれるようになります。
ソリューション エクスプローラーで、[CDemoLib] を右クリックし、ショートカット メニューの [プロパティ] をクリックします。[既定の名前空間] ボックスに CDemoLib が含まれていることを確認します。
ルート名前空間は、アセンブリ内のコンポーネント名の修飾に使用されます。たとえば、CDemo という名前のコンポーネントが 2 つのアセンブリに含まれる場合は、CDemoLib.CDemo という形で目的の CDemo コンポーネントを指定できます。
ダイアログ ボックスを閉じます。
[プロジェクト] メニューの [コンポーネントの追加] をクリックします。
[新しい項目の追加] ダイアログ ボックスの [コンポーネント クラス] をクリックし、[ファイル名] ボックスに「CDemo.cs」と入力します。[追加] をクリックしてコンポーネントを作成します。
CDemo という名前のコンポーネントが、クラス ライブラリに追加されます。
ソリューション エクスプローラーで、[CDemo.cs] を右クリックし、ショートカット メニューの [コードの表示] をクリックします。コード エディターが表示されます。
public partial class CDemo のすぐ後に : Component が表示されています。この部分は、クラスの継承元のクラスを示しています。既定では、コンポーネントはシステムによって提供される Component クラスを継承します。Component クラスには、デザイナーを使う機能を含め、コンポーネントのための多くの機能が用意されています。
ソリューション エクスプローラーで、[Class1.cs] を右クリックし、[削除] をクリックします。これにより、クラス ライブラリで提供される既定のクラスが削除されます。このクラスは、このチュートリアルでは使用しません。
[ファイル] メニューの [すべてを保存] をクリックしてプロジェクトを保存します。
コンストラクターとデストラクターの追加
コンストラクターは、コンポーネントの初期化方法を制御します。Finalize メソッドは、コンポーネントの終了方法を制御します。CDemo クラスのコンストラクターと Finalize メソッドのコードには、存在する CDemo オブジェクトの現在の個数が保持されています。
CDemo クラスのコンストラクターとデストラクターのコードを追加するには
コード エディターで、CDemo クラスのインスタンスの現在の個数と、各インスタンスの ID 番号を保持するメンバー変数を追加します。
public readonly int InstanceID; private static int NextInstanceID = 0; private static long ClassInstanceCount = 0;
InstanceCount メンバー変数と NextInstanceID メンバー変数は static と宣言されるため、これらの変数はクラス レベルでだけ存在します。これらのメンバーにアクセスする CDemo のインスタンスはすべて、メモリ上の同じ場所を使用します。静的メンバーは、コード内で CDemo クラスが最初に参照されたときに初期化されます。たとえば、CDemo オブジェクトが最初に作成されたときや、静的メンバーの 1 つが最初にアクセスされたときなどです。
CDemo クラスの既定のコンストラクターである public CDemo() と public CDemo(IContainer container) を見つけます。Visual C# では、すべてのコンストラクターがクラスと同じ名前になります。コンポーネントにはパラメーターの異なるいくつかのコンストラクターを定義できますが、どのコンストラクターにもコンポーネントと同じ名前を付ける必要があります。
[!メモ]
クラスのインスタンスを作成できるクライアントは、コンストラクターのアクセス レベルによって決まります。
新しい CDemo が作成されたときにインスタンス数をインクリメントし、インスタンス ID 番号を設定するために、public CDemo() に次のコードを追加します。
[!メモ]
コードは必ず InitializeComponent の呼び出しの後に追加してください。この呼び出しの時点で、含まれるコンポーネントがすべて初期化されます。
InstanceID = NextInstanceID ++; ClassInstanceCount ++;
InstanceID は readonly メンバーであるため、コンストラクター内でしか設定できません。
[!メモ]
マルチスレッドについてよく理解すると、InstanceID の割り当てと NextInstanceID のインクリメントを分割できない操作にする必要があることがわかります。スレッド処理に関連するこの問題、およびその他の問題については、「チュートリアル : Visual C# による簡単なマルチスレッド コンポーネントの作成」を参照してください。
コンストラクターの末尾の後に次のメソッドを追加します。
~CDemo() { ClassInstanceCount --; }
このメソッドはデストラクターと呼ばれ、クラス名の前にティルダ (~) を付けて示されます。メモリ マネージャーは、CDemo オブジェクトによって占有されていたメモリを最後にクリアする直前に、デストラクターを呼び出します。デストラクターを実装することにより、コンポーネントがメモリから削除される直前にクリーンアップを実行できます。ただし、このチュートリアルで後述するように、リソースを早期に解放した方がよい理由がいくつかあります。
クラスへのプロパティの追加
CDemo クラスには 1 つのプロパティしかありません。これは、ある時点でメモリ内に存在する CDemo オブジェクトの数をクライアントが判別するための静的プロパティです。メソッドも同様な方法で作成できます。
CDemo クラスのプロパティを作成するには
CDemo クラスに次のプロパティ宣言を追加して、クライアントが CDemo のインスタンスの数を取得できるようにします。
public static long InstanceCount { get { return ClassInstanceCount; } }
コンポーネントのテスト
コンポーネントをテストするには、そのコンポーネントを使用するプロジェクトが必要です。このプロジェクトは、[実行] ボタンを押したときに最初に起動するプロジェクトであることが必要です。
CDemoTest クライアント プロジェクトをソリューションのスタートアップ プロジェクトとして追加するには
[ファイル] メニューの [追加] をポイントし、[新しいプロジェクト] をクリックして [新しいプロジェクトの追加] ダイアログ ボックスを表示します。
[Windows アプリケーション] プロジェクト テンプレートを選択し、[プロジェクト名] ボックスに「CDemoTest」と入力して [OK] をクリックします。
ソリューション エクスプローラーで [CDemoTest] を右クリックし、ショートカット メニューの [スタートアップ プロジェクトに設定] をクリックします。
CDemo コンポーネントを使用するには、クライアント テスト プロジェクトにクライアント ライブラリ プロジェクトへの参照が含まれる必要があります。参照を追加した後で、テスト アプリケーションに using ステートメントを追加すると、コンポーネントの使用を単純化できます。
クラス ライブラリ プロジェクトへの参照を追加するには
ソリューション エクスプローラーで、[CDemoTest] のすぐ下にある [参照設定] ノードを右クリックし、ショートカット メニューの [参照の追加] をクリックします。
[参照の追加] ダイアログ ボックスの [プロジェクト] タブをクリックします。
[CDemoLib] クラス ライブラリ プロジェクトをダブルクリックします。CDemoTest プロジェクトの [参照設定] ノードの下に CDemoLib が表示されます。
ソリューション エクスプローラーで [Form1.cs] を右クリックし、ショートカット メニューの [コードの表示] をクリックします。
CDemoLib への参照を追加することにより、CDemo コンポーネントの完全修飾名 CDemoLib.CDemo を使用できます。
using ステートメントを追加するには
Form1 のコード エディターの一番上にある using ステートメントの一覧に、次の using ステートメントを追加します。
using CDemoLib;
using ステートメントを追加することにより、ライブラリ名を省略でき、コンポーネントを CDemo として参照できます。
これで、コンポーネントをテストするためのテスト プログラムを作成して使用できます。
オブジェクトの有効期間
CDemoTest プログラムでは、多数の CDemo オブジェクトの作成と解放によって、.NET Framework におけるオブジェクトの有効期間を示しています。
CDemo オブジェクトを作成および解放するコードを追加するには
[Form1.cs[デザイン]] をクリックして、デザイナーに戻ります。
ツールボックスの [すべての Windows フォーム] タブから、Button と Timer を Form1 のデザイン サーフェイスにドラッグします。
非可視の Timer コンポーネントが、フォームの下の別のデザイン サーフェイスに表示されます。
timer1 のアイコンをダブルクリックして、timer1 コンポーネントの Tick イベントに対するイベント処理メソッドを作成します。イベント処理メソッドに次のコードを追加します。
this.Text = "CDemo instances: " + CDemo.InstanceCount;
タイマーが時を刻むごとに、フォームのキャプションには、現在の CDemo クラスのインスタンスの数が表示されます。クラス名は、静的 InstanceCount プロパティの修飾子として使用されています。静的メンバーにアクセスするために CDemo のインスタンスを作成する必要はありません。
Form1 (public Form1()) のコンストラクターを探し、InitializeComponent() の呼び出しの後に次のコードを追加します。
timer1.Enabled = true;
これにより、フォームが作成されるとすぐにタイマーが起動されます。
[Form1.cs [デザイン]] タブをクリックして、デザイナーに戻ります。
Form1 の Button をダブルクリックして、そのボタンの Click イベントのイベント処理メソッドを作成します。イベント処理メソッドに次のコードを追加します。
CDemo cd; int ct; for (ct = 0; ct < 1000; ct++) cd = new CDemo();
このコードは理解しにくい場合があります。CDemo の各インスタンスが作成されるたびに、前のインスタンスは解放されます。for ループが終了すると、CDemo のインスタンスは 1 つだけ残ります。イベント処理メソッドが終了すると、変数 cd がスコープ外になるため、そのインスタンスも解放されます。
推測してわかるように、この例は現実の流れとは違います。
CDemoTest プロジェクトと CDemo プロジェクトを実行してデバッグするには
F5 キーを押してソリューションを起動します。
クライアント プロジェクトが起動し、Form1 が表示されます。フォームのキャプションが "CDemo instances: 0" となっていることを確認します。
ボタンをクリックします。フォームのキャプションが "CDemo instances: 1000" となります。
CDemo のインスタンスは、ボタンの Click イベント処理プロシージャが終了するまでにすべて解放されています。これらはなぜ終了されなかったのでしょうか。簡単に言うと、メモリ マネージャーがバックグラウンドでオブジェクトを終了させる優先順位が低いためです。この優先順位は、システムがメモリ不足になった場合にだけ高くなります。このような "最もロードの少ない" ガベージ コレクション方式により、オブジェクトを高速に割り当てることができます。
ボタンをさらに数回クリックしてキャプションを確認します。ある時点で、インスタンスの数が突然少なくなります。これは、メモリ マネージャーが一部のオブジェクトのメモリをクリアしたことを意味します。
[!メモ]
10 回を超えてクリックしても CDemo のインスタンス数が減らない場合は、メモリの使用量を増やすようにコードの調整が必要になる場合があります。フォームを閉じて開発環境に戻り、for ループの繰り返し回数を 10000 に増やします。その後、プロジェクトを再実行します。
手順 3. を繰り返します。今度はインスタンス数がさらに減り、メモリ マネージャーはより多くのオブジェクトを終了します。
実際には、手順 3. を繰り返すたびに、メモリ マネージャーにかかわりなく、より多くの CDemo オブジェクトが割り当てられるようになります。これは、Visual Studio のより多くの部分がスワップ アウトされ、CDemo のインスタンス用に多くのメモリ領域が残されるためです。
フォームを閉じて開発環境に戻ります。