チュートリアル : Visual Basic によるコンポーネントの作成
更新 : 2007 年 11 月
コンポーネントは、再利用できるコードをオブジェクトの形式で提供します。オブジェクトを作成し、オブジェクトのプロパティやメソッドを呼び出すことによってコンポーネントのコードを使用するアプリケーションは、クライアントと呼ばれます。クライアントは、使用するコンポーネントと同じアセンブリに含まれている場合もあれば、別の場所に存在する場合もあります。
次の手順は互いに関連するため、実行順序が重要です。
メモ : |
---|
使用している設定またはエディションによっては、表示されるダイアログ ボックスやメニュー コマンドがヘルプに記載されている内容と異なる場合があります。設定を変更するには、[ツール] メニューの [設定のインポートとエクスポート] をクリックします。詳細については、「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 で採用されていたものとは異なります。構文の変更の詳細については、「プロパティ プロシージャの変更点 (Visual Basic 6.0 ユーザー向け)」を参照してください。
コンポーネントのテスト
コンポーネントをテストするには、そのコンポーネントを使用するプロジェクトが必要です。このプロジェクトは、[実行] ボタンを押したときに最初に起動するプロジェクトであることが必要です。
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 のインスタンス用に多くのメモリ領域が残されるためです。
フォームを閉じて開発環境に戻ります。