チュートリアル : Visual Basic によるコンポーネントの作成
コンポーネントは、再利用できるコードをオブジェクトの形式で提供します。オブジェクトを作成し、オブジェクトのプロパティやメソッドを呼び出すことによってコンポーネントのコードを使用するアプリケーションは、クライアントと呼ばれます。クライアントは、使用するコンポーネントと同じアセンブリに含まれている場合もあれば、別の場所に存在する場合もあります。
次の手順は互いに関連するため、実行順序が重要です。
[!メモ]
実際に画面に表示されるダイアログ ボックスとメニュー コマンドは、アクティブな設定またはエディションによっては、ヘルプの説明と異なる場合があります。設定を変更するには、[ツール] メニューの [設定のインポートとエクスポート] をクリックします。詳細については、「Visual Studio での開発設定のカスタマイズ」を参照してください。
プロジェクトの作成
CDemoLib クラス ライブラリと CDemo コンポーネントを作成するには
[ファイル] メニューの [新規作成] をクリックし、[プロジェクト] をクリックして [新しいプロジェクト] ダイアログ ボックスを開きます。Visual Basic プロジェクトの種類一覧の [クラス ライブラリ] プロジェクト テンプレートを選択し、[プロジェクト名] ボックスに「CDemoLib」と入力します。
[!メモ]
新しいプロジェクトを作成するときには、必ずプロジェクトの名前を指定します。これにより、ルート名前空間、アセンブリ名、およびプロジェクト名が設定されます。また、既定のコンポーネントが正しい名前空間に含まれるようになります。
ソリューション エクスプローラーで、[CDemoLib] を右クリックし、ショートカット メニューの [プロパティ] をクリックします。[ルート名前空間] ボックスに CDemoLib が含まれていることを確認します。
ルート名前空間は、アセンブリ内のコンポーネント名の修飾に使用されます。たとえば、CDemo という名前のコンポーネントが 2 つのアセンブリに含まれる場合は、CDemoLib.CDemo という形で目的の CDemo コンポーネントを指定できます。
ダイアログ ボックスを閉じます。
[プロジェクト] メニューの [コンポーネントの追加] をクリックします。[新しい項目の追加] ダイアログ ボックスの [コンポーネント クラス] をクリックし、[ファイル名] ボックスに「CDemo.vb」と入力します。CDemo という名前のコンポーネントが、クラス ライブラリに追加されます。
ソリューション エクスプローラーで、[すべてのファイルを表示] をクリックします。[CDemo.vb] ノードを開き、[CDemo.Designer.vb] ファイルを表示します。[CDemo.Designer.vb] を右クリックし、ショートカット メニューの [コードの表示] をクリックします。コード エディターが表示されます。
Partial Public Class CDemo のすぐ下に Inherits System.ComponentModel.Component が表示されています。この部分は、クラスの継承元のクラスを示しています。既定では、コンポーネントはシステムによって提供される Component クラスを継承します。Component クラスには、デザイナーを使う機能を含め、コンポーネントのための多くの機能が用意されています。
Public Sub New() を探します。メソッド本体全体を選択し、Ctrl キーを押しながら X キーを押すことで、メソッドを CDemo.Designer.vb ファイルから切り取ります。
ソリューション エクスプローラーで、[CDemo.vb] を右クリックし、ショートカット メニューの [コードの表示] をクリックします。コード エディターが表示されます。
選択内容を CDemo クラスの本体に貼り付けます。これによって、デザイナーに干渉されることなく新規の作業ができます。
ソリューション エクスプローラーで、[Class1.vb] を右クリックし、[削除] をクリックします。これにより、クラス ライブラリで提供される既定のクラスが削除されます。このクラスは、このチュートリアルでは使用しません。
[ファイル] メニューの [すべてを保存] をクリックしてプロジェクトを保存します。
コンストラクターとファイナライザーの追加
コンストラクターは、コンポーネントの初期化方法を制御します。Finalize メソッドは、コンポーネントの終了方法を制御します。CDemo クラスのコンストラクターと Finalize メソッドのコードには、存在する CDemo オブジェクトの現在の個数が保持されています。
CDemo クラスのコンストラクターとファイナライザーのコードを追加するには
コード エディターで、CDemo クラスのインスタンスの現在の個数と、各インスタンスの ID 番号を保持するメンバー変数を追加します。
Public ReadOnly InstanceID As Integer Private Shared NextInstanceID As Integer = 0 Private Shared ClassInstanceCount As Long = 0
InstanceCount メンバー変数と NextInstanceID メンバー変数は Shared と宣言されるため、これらの変数はクラス レベルでだけ存在します。これらのメンバーにアクセスする CDemo のインスタンスはすべて、メモリ上の同じ場所を使用します。共有メモリは、コード内で CDemo クラスが最初に参照されたときに初期化されます。たとえば、CDemo オブジェクトが最初に作成されたときや、共有メンバーの 1 つが最初にアクセスされたときなどです。
CDemo クラスの既定のコンストラクターである Public Sub New() と Public Sub New(Container As System.ComponentModel.IContainer) を見つけます。Visual Basic ではすべてのコンストラクターに New という名前が付けられます。コンポーネントにはパラメーターの異なるいくつかのコンストラクターを使用できますが、それらの名前にはすべて New が含まれている必要があります。
[!メモ]
クラスのインスタンスを作成できるクライアントは、クラスのコンストラクターのアクセス レベルによって決まります。他のバージョンの Visual Basic では、オブジェクト作成は Instancing プロパティで制御されていました。Instancing プロパティを使用していた場合は、「Visual Basic .NET におけるコンポーネントのインスタンス化の変更点」を参照することをお勧めします。
新しい CDemo が作成されたときにインスタンス数をインクリメントし、インスタンス ID 番号を設定するために、Sub New() に次のコードを追加します。
[!メモ]
コードは必ず InitializeComponent の呼び出しの後に追加してください。この呼び出しの時点で、含まれるコンポーネントがすべて初期化されます。
InstanceID = NextInstanceID NextInstanceID += 1 ClassInstanceCount += 1
InstanceID は ReadOnly メンバーであるため、コンストラクター内でしか設定できません。
[!メモ]
マルチスレッドについてよく理解すると、InstanceID の割り当てと NextInstanceID のインクリメントを分割できない操作にする必要があることがわかります。スレッド処理に関連するこの問題、およびその他の問題については、「チュートリアル : Visual Basic による簡単なマルチスレッド コンポーネントの作成」を参照してください。
コンストラクターの末尾の後に次のメソッドを追加します。
Protected Overrides Sub Finalize() ClassInstanceCount -= 1 End Sub
メモリ マネージャーでは、CDemo オブジェクトによって占有されていたメモリを最後にクリアする直前に、Finalize を呼び出します。Finalize メソッドは、.NET のクラス階層構造内のすべての参照型のルートである Object に定義されています。Finalize をオーバーライドすることにより、コンポーネントがメモリから削除される直前にクリーンアップを実行できます。ただし、このチュートリアルで後述するように、リソースを早期に解放した方がよい理由がいくつかあります。
クラスへのプロパティの追加
CDemo クラスには 1 つのプロパティしかありません。これは、ある時点でメモリ内に存在する CDemo オブジェクトの数をクライアントが判別するための共有プロパティです。メソッドも同様な方法で作成できます。
CDemo クラスのプロパティを作成するには
CDemo クラスに次のプロパティ宣言を追加して、クライアントが CDemo のインスタンスの数を取得できるようにします。
Public Shared ReadOnly Property InstanceCount() As Long Get Return ClassInstanceCount End Get End Property
[!メモ]
プロパティ宣言の構文は、以前のバージョンの Visual Basic で採用されていたものとは異なります。構文の変更の詳細については、「Property Procedure Changes for Visual Basic 6.0 Users」を参照してください。
コンポーネントのテスト
コンポーネントをテストするには、そのコンポーネントを使用するプロジェクトが必要です。このプロジェクトは、[実行] ボタンを押したときに最初に起動するプロジェクトであることが必要です。
CDemoTest クライアント プロジェクトをソリューションのスタートアップ プロジェクトとして追加するには
[ファイル] メニューの [追加] をポイントし、[新しいプロジェクト] をクリックして [新しいプロジェクトの追加] ダイアログ ボックスを表示します。
[Windows アプリケーション] プロジェクト テンプレートを選択し、[プロジェクト名] ボックスに「CDemoTest」と入力して [OK] をクリックします。
ソリューション エクスプローラーで [CDemoTest] を右クリックし、ショートカット メニューの [スタートアップ プロジェクトに設定] をクリックします。
CDemo コンポーネントを使用するには、クライアント テスト プロジェクトにクライアント ライブラリ プロジェクトへの参照が含まれる必要があります。参照を追加した後で、テスト アプリケーションに Imports ステートメントを追加すると、コンポーネントの使用を単純化できます。
クラス ライブラリ プロジェクトへの参照を追加するには
ソリューション エクスプローラーで、[すべてのファイルを表示] をクリックします。[CDemoTest] のすぐ下にある [参照設定] ノードを右クリックし、ショートカット メニューの [参照の追加] をクリックします。
[参照の追加] ダイアログ ボックスの [プロジェクト] タブをクリックします。
[CDemoLib] クラス ライブラリ プロジェクトをダブルクリックします。CDemoTest プロジェクトの [参照設定] ノードの下に CDemoLib が表示されます。
ソリューション エクスプローラーで [Form1.vb] を右クリックし、ショートカット メニューの [コードの表示] をクリックします。
CDemoLib への参照を追加することにより、CDemo コンポーネントの完全修飾名 CDemoLib.CDemo を使用できます。
Imports ステートメントを追加するには
Form1 のコード エディターの先頭 (Class 宣言の前) に次の Imports ステートメントを追加します。
Imports CDemoLib
Imports ステートメントを追加することにより、ライブラリ名を省略でき、コンポーネントを CDemo として参照できます。Imports ステートメントの詳細については、「Visual Basic における名前空間」を参照してください。
これで、コンポーネントをテストするためのテスト プログラムを作成して使用できます。
オブジェクトの有効期間
CDemoTest プログラムでは、多数の CDemo オブジェクトの作成と解放によって、.NET Framework におけるオブジェクトの有効期間を示しています。
CDemo オブジェクトを作成および解放するコードを追加するには
[Form1.vb[デザイン]] をクリックして、デザイナーに戻ります。
ツールボックスの [すべての Windows フォーム] タブから、Button と Timer を Form1 のデザイン サーフェイスにドラッグします。
非可視の Timer コンポーネントが、フォームの下の別のデザイン サーフェイスに表示されます。
Timer1 のアイコンをダブルクリックして、Timer1 コンポーネントの Tick イベントに対するイベント処理メソッドを作成します。イベント処理メソッドに次のコードを追加します。
Me.Text = "CDemo instances: " & CDemo.InstanceCount
タイマーが時を刻むごとに、フォームのキャプションには、現在の CDemo クラスのインスタンスの数が表示されます。クラス名は、共有 InstanceCount プロパティの修飾子として使用されています。共有メンバーにアクセスするために CDemo のインスタンスを作成する必要はありません。
[Form1.vb [デザイン]] タブをクリックして、デザイナーに戻ります。
Timer1 を右クリックし、ショートカット メニューの [プロパティ] をクリックします。[プロパティ] ウィンドウで、Enabled プロパティの値を True に設定します。これにより、フォームが作成されるとすぐにタイマーが起動されます。
Form1 の Button をダブルクリックして、そのボタンの Click イベントのイベント処理メソッドを作成します。イベント処理メソッドに次のコードを追加します。
Dim cd As CDemo Dim ct As Integer For ct = 1 To 1000 cd = New CDemo Next
このコードは理解しにくい場合があります。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 のインスタンス用に多くのメモリ領域が残されるためです。
フォームを閉じて開発環境に戻ります。