長谷川 裕行 (はせがわ ひろゆき)
有限会社 手國堂 代表取締役
http://www.hirop.com/
テクニカルライターとして活躍。プログラミングに関する著書多数、DB Magazineなどにも多くの記事を提供している。 |
これまで、基本的に「1つのフォームに1つのコードモジュール」というスタイルを前提に説明を進めてきました。しかし実際には、たくさんのフォームを使って複雑な処理を組み上げる機会が多くあります。そのようなときには、他のコードモジュールに記述された変数やプロシージャを利用する必要も生じます。
たくさんのフォームを使った場合に必要な、情報の相互参照について知っておきましょう。
プロシージャとその中で扱う変数は、プログラム内のどこからでも見通せるわけではありません。不用意に中を書き換えられないよう、外からはアクセスできない仕組みとなっています。
- プロシージャとモジュール -
VBのソースコードは、プロシージャの集合です。プロシージャとは「何らかの処理を行うための命令の集合」であり、一定の意味を持った「機能」と捉えられます。
プロシージャがいくつか集まったものを「コードモジュール」と呼びます。コードモジュールはプログラム内部の機能を実現し、フォームモジュールがプログラムの外見を定義します。
フォームモジュールにはコードモジュールが必要ですが、フォームモジュールと連携しないコードモジュールも存在します。これを標準モジュールといいます。標準モジュールは、フォームという外見を持たない機能の集合体です。
つまりVBで作ったプログラムは
命令とデータの集合:プロシージャ
→プロシージャとデータの集合:モジュール
という二重構造で出来上がっていることになります。
図1:VBによるアプリケーションの構造
- パソコン部品とプログラムの構造 -
プロシージャもモジュールも処理の集合であり、それ自体で意味的に完結した存在です。その完結した処理単位が集まり、互いに連携してより大きくより具体的な処理を構築することになります。
この構造は、例えばパソコン(AT互換機)を思い浮かべれば、具体的なイメージがつかめると思います。プロシージャはチップ(集積回路)、モジュールはインターフェイスカードやその他の機器に当てはまります。
ビデオカードはグラフィック画像をディスプレイに送る機能を持っていますが、その元になる画像データを作ることはできません。CPUや他の回路、ソフトウェアなどと協調して動作することで、その機能が発揮されるのです。
- 情報の出入口と垣根 -
他の機能と協調するということは、それぞれが情報をやり取りするということです。それぞれの機能の中にも、内部で処理するための情報が必要です。が、それは外(他の機能)に伝える必要はありません。機能の中で処理した結果の情報だけを、外に出せばよいのです。
パソコンのインターフェイスカードなど現実の機器類なら、外との情報の出入り口は端子部分が受け持ちます。プログラムではそのような物理的な仕組みは実装できないので、論理的な「情報の垣根」が必要になります。
VBで作るプログラムではプロシージャとモジュールが垣根となり、他のプロシージャやモジュールからアクセスできるかどうかを規定できます。これを「適用範囲」または「通用範囲」と呼びます。
- ローカル変数 -
あるプロシージャの中でDimステートメントを使って宣言した変数は、そのプロシージャの中だけでしか使用できません。この変数をローカル変数(局所変数)と呼び、プロシージャが呼び出されてDimステートメントが実行されると、変数のための領域(実体)がメモリに確保されて初期化されます。
プロシージャ内の処理が終わると変数のための領域は解放され、これによって変数は消滅します。つまりローカル変数は、プロシージャが呼び出されるたびに新たに確保され、用が済んだら消える…という短命な変数です。
コードで変数名を指定してその値を利用することを、(変数を)「参照する」と言います。ローカル変数は、他のプロシージャから参照できません。これは、ローカル変数の寿命を考えれば当然です。
宣言されたプロシージャ内でしか存在し得ないので、そのプロシージャ以外のプロシージャが実行されている時点では、参照しようにもできるわけがありません。
- やり取りは引数と戻り値で -
ローカル変数は、用が済めば捨ててしまう変数です。多くの変数はプロシージャの中だけで存在していれば、その役割を果たせます。他のプロシージャに情報を渡したい場合は、呼び出す場合には引数、処理結果なら戻り値を利用すれば済んでしまいます。
引数と戻り値以外に情報の受け渡し口があると、管理が複雑になってしまいます。インターフェイスカードで言えば、スロットに差し込む端子の他にも、カード上のあちこちにコネクタやソケットが付いているようなものです。これでは、どこに何を差し込めばよいのか分かりません。
- 値を保持するローカル変数 -
ローカル変数がプロシージャとともに消滅してしまうと、都合が悪い場合もあります。
例えば、あるプロシージャが呼び出された回数をカウントする場合、
のように宣言しても、役には立ちません。この変数は「プロシージャが呼び出されるたびに初期化される」ため、呼び出された回数を数え上げることはできないのです。
このような機能を実現するためにはプロシージャ内でStaticステートメントを使って変数を宣言します。
Static intCount As Integer |
こうして宣言した変数は、垣根はあくまでプロシージャ単位であり、他のプロシージャから参照することはできませんが、変数を宣言したプロシージャが終了してもメモリから削除されず、直前の値を保持し続けます。
- グローバル変数 -
変数は、モジュールの宣言セクション(General-declarations)でも宣言できます。
こうして宣言された変数は、モジュールが存在している間存在し続ける長命な変数で、モジュール内のすべてのプロシージャから参照できます。これをグローバル変数(広域変数)といいます。
一見非常に便利ですが、モジュール内のどこからでも値を変更できてしまうため、多用すると思わぬ誤動作(バグ)を引き起こすことがあります。
- ローカルとグローバルの違い -
ローカル変数とグローバル変数の働きの違いを、簡単なアプリケーションで見てみましょう。
ラベルlblCountをクリックした回数を変数に保存し、テキストボックスtxtCountに表示する――という処理です。
リスト1は、プロシージャ内でDimステートメントを使って宣言したローカル変数intCountに保存させるものです。この方法では、プロシージャが呼び出されるたびに変数intCountが初期化されるため、テキストボックスには常に「1」としか表示されません。
リスト2は、プロシージャ内でStaticステートメントを使って宣言したローカル変数intCountに保存させるものです。これなら、変数intCountは先に呼び出されたときの値を保持しているため、テキストボックスにはクリックされた回数が正しく表示されます。
リスト3では、グローバル変数intGlbCountを使っています。この方法でも正しく表示されますが、宣言セクションで変数を宣言するため、プロシージャの実体(定義箇所)と変数の宣言箇所が離れてしまいます。
リスト1:Dimステートメントで変数を宣言
Private Sub lblCount_Click()
Dim intCount As Integer
' カウンタを1つ増やす
intCount = intCount + 1
' カウンタを表示
txtCount.Text = CStr(intCount)
End Sub
|
リスト2:Staticステートメントで変数を宣言
Private Sub lblCount_Click()
Static intCount As Integer
' カウンタを1つ増やす
intCount = intCount + 1
' カウンタを表示
txtCount.Text = CStr(intCount)
End Sub
|
|
|
リスト3:グローバル変数を使用
Dim intGlbCount As Integer
Private Sub lblCount_Click()
' カウンタを1つ増やす
intGlbCount = intGlbCount + 1
' カウンタを表示
txtCount.Text = CStr(intGlbCount)
End Sub
|
この処理のサンプルを用意してありますので、ダウンロードして動作を試してください。
画面1:サンプル・アプリケーションex26.exeの実行画面
- ローカルとグローバルの使い分け -
ローカル変数はメモリを有効に使用できますが、常に垣根を意識しなければならないため、機能的な制約が付きまといます。では、すべての変数をグローバル変数としてしまえばよいかと言えば、そうではありません。
モジュール内のどこからでも参照できてしまうグローバル変数は、間違いまでグローバルにしてしまいます。あるプロシージャにバグがあって変数の値を間違って設定した場合、その変数を参照するほかのプロシージャの処理結果も間違ってしまいます。
このようにグローバル変数は、箇所の変更を、あちこちに飛び火させる可能性をはらんでいるのです。
グローバル変数は、
複数のプロシージャから参照する必要のある
文字どおりグローバルな情報だけに使う
ようにします。
それ以外の変数は、すべてローカル変数とし、他のプロシージャに値を渡したい場合は、先述したように引数と戻り値を利用します。
変数と同じように、プロシージャにも垣根があります。プロシージャの垣根は、モジュール単位となります。
- モジュールの垣根 -
プロシージャの適用範囲は、モジュール単位の垣根となります。ここで言うモジュールとは、コードモジュールです。コードモジュールはフォームモジュールと連携しているため、プロシージャの垣根はフォームの垣根でもあります。
通常のプロシージャは、定義したモジュールの中からしか参照できません。同じモジュール内の他のプロシージャからは呼び出せますが、他のモジュールからは呼び出せないのです。
多くのプロシージャはフォーム上のコントロールが発生するイベントに対応した、イベントプロシージャです。そうでないプロシージャは、イベントプロシージャから呼び出される下請けのプロシージャとなるでしょう。
そのため、他のフォームと連携しているコードモジュールから、参照される必要はありません。基本的に、プロシージャはモジュールという垣根で囲まれ、外からは見えない状態で構わないのです。
- PrivateプロシージャとPublicプロシージャ -
しかし、ときには他のモジュールから呼び出さなければならない処理もあります。全般的に共通する下請け処理は、その典型でしょう。
たくさんのフォームを使いながら、1つのデータベースファイルだけをアクセスするようなアプリケーションでは、データベースのオープンやレコードセットの生成など基本的な処理は、様々なモジュールで必要になるでしょう。
そのような場合には、モジュールをPublicキーワードを付けて定義します。Publicを付けて定義されたプロシージャは、他のモジュール内のプロシージャから呼び出すことができます。これをPublicプロシージャといいます。
それ以外の、他のモジュールからは隠蔽されたプロシージャは、Privateプロシージャといいます。本来ならPrivateキーワードを付けて定義しなければなりませんが、これは省略できます。
つまり、プロシージャにはPrivateとPublicの2種類が存在するのです。
Privateプロシージャ:定義したモジュール内でしか参照されません。
Publicプロシージャ:他のモジュールからも参照できます。
グローバル変数もプロシージャと同じように、PrivateとPublicキーワードによって適用範囲を規定できます。
Privateキーワードを付けて宣言すると、モジュール内のプロシージャからだけ参照できます。これをPrivate変数といいます。
Publicキーワードを付けて宣言すると、他のモジュール内にプロシージャからも参照できます。これをPublic変数といいます。
Private/Publicを省略すると、Private変数とみなされます。
図2:変数とプロシージャの適用範囲
VBでは、フォームモジュールによって自動的に垣根が作られます。そのため「垣根」を意識する機会は少なくなりますが、まったく意識しない訳にはいきません。垣根の意味と役割を理解しておきましょう。
- モジュール単位の垣根が重要 -
先述したように、プロシージャを用途ごと、機能ごとに分類していくと、複数のモジュールで共用したいプロシージャもできあがるはずです。
変数の場合も、ローカル変数では同じモジュール内でさえ参照できないため、必要な場合はグローバル変数とします。それでも他のモジュールからは参照できないため、プログラム全体から参照したい変数は、Public変数として宣言します。
コードモジュールはフォームモジュールと密接に関連していることを思い出してください。VBによるアプリケーションは、基本的にフォーム単位で処理の区切りが付けられています。
個々のモジュール内でPublicプロシージャやPublic変数を多用すると、特定モジュールが他のモジュールに窓口を開いてしまうようになります。つまり「フォーム単位で独立した機能」という前提が、ここで崩れてしまうのです。
- 標準モジュールを利用する -
そこで、プログラム全体から参照される変数やプロシージャは、できる限り標準モジュールにまとめるようにします。
標準モジュールは関連するフォームを持たない特別なコードモジュールで、たくさんのフォームを使った大規模なアプリケーションで、複数のコードモジュールから参照される共通のプロシージャや変数を定義する場所です。
これを作成するには、メニューから「プロジェクト(P)」→「標準モジュールの追加(M)」を選択します。また、プロジェクトウィンドウでプロジェクトを選択し、コンテキストメニュー(右ボタンメニュー)で「追加(A)」→「標準モジュール(M)」を選択しても構いません。
標準モジュールの利用については、次回以降、回を追って紹介します。
- 名前の競合 -
ローカル変数はプロシージャ内でしか使用されないため、異なるプロシージャ内に同名のローカル変数があっても問題ありません。Staticステートメントで宣言したローカル変数も、呼び出しのたびに初期化されないというだけで、ローカル変数には変わりないため、同じことが言えます。
グローバル変数は、Publicキーワードを付けない限りモジュール内でしか参照されないため、他のモジュールに同じ名前の変数があっても構いません。
プロシージャの場合も、Privateプロシージャは他のモジュールから参照されないため、他のモジュールに同名のプロシージャが存在しても構いません。
Publicキーワードを付けたグローバル変数とプロシージャは、アプリケーション内で一位となります。従ってこれらは、同名の変数やプロシージャを他に宣言・定義できません。
これで、VBプログラミングの基本的な解説はひととおり終了しました。次回からは、大規模なプログラミングに必要なモジュール別開発、業務アプリに不可欠な帳票の印刷処理など、さらに実用的な開発テクニックを取り上げていきます。
VBプロジェクトファイルのダウンロード
(LZH形式 4.91KB)
|