今回は変数のスコープについて。
スコープというのは変数の有効範囲のことで、宣言された場所から参照できなくなる場所までを指す。
このスコープはVB.NetとVBAでは大きく異なる。
たとえば、VBAで以下のコードはOption Explicitをつけていたとしても普通に実行できてしまう。
Sub Main() If True Then Dim x As Integer: x = 1 End If Debug.Print x End Sub
しかし、同じように以下のコードをVB.Netで実行すると、
Sub Main() If True Then Dim x As Integer: x = 1 End If Console.Write(x) Console.ReadKey(True) End Sub
「'x' は宣言されていません。保護レベルが原因でアクセスできない可能性もあります。」というエラーになってしまう。
これは、If文のブロック内で宣言されたxは、同じブロック内でしか使えないということ。
だから、Ifの外にあるConsole.Write(x)で参照しようとしたときにxが無いと言われてしまう。
VBAのローカル変数のスコープは、宣言されてからEnd SubまたはEnd Functionまで、VB.Netのローカル変数のスコープは、宣言されたブロック内となる。
ちなみに、Ifの外側で宣言した場合は、Ifの中からでもアクセスできる。中から外は見えるが、外から中は見えないということ。
スコープが狭いことのメリットは、変数の影響を限定できるということである。
たとえば次のコードで、2つのiは完全に別物である。したがって、下例のように別の型にすることもできる。
(VB.Netの場合、Dimの代わりに「For 変数 As 型名」で宣言したことになる。)
Sub Main() For i As Integer = 1 To 10 Step 1 Console.WriteLine(i) Next For i As Double = 0.5 To 1 Step 0.1 Console.WriteLine(i) Next Console.ReadKey(True) End Sub
VBAではどうかというと、もちろんエラーになる。
同じことをするには、それぞれループの外側で変数宣言をしてやらないといけない。
Sub Main() Dim i As Integer For i = 1 To 10 Step 1 Debug.Print i Next Dim j As Double For j = 0.5 To 1 Step 0.1 Debug.Print j Next End Sub
そして、スコープはEnd Subまでなので、使い終わったあとも変数を参照できてしまう。
これの何が問題なのかいまいちピンとこないかもしれないが、根底にあるのは「してはいけないことは、できなくしておく」という考え方である。
たとえば、変数だけあれば、定数なんて無くてもプログラミングは可能だ。
しかし、バグを埋め込まないためには、変更してはいけないものは定数で宣言するべきである。
また、参照してはいけない場所ではそもそも参照できなくしておくのが安全な設計と言える。
その変数はある特定の用途にだけ使うつもりだったのに、変なところで参照されると依存関係ができてしまい、自由に変更できなくなる。そういうメンテナンス上のコストにもつながる。
だから変数の影響はできるだけ局所的にしておくこと。
別プロシージャへ値を渡すときにグローバル変数じゃなくて引数にするのも、変数の影響を局所に閉じ込めるためである。
VBAには残念ながらスコープを限定する機能は無いが、使う直前で変数宣言する、変数宣言と同時に初期化する、使い終わった変数は、変なところから参照しないという原則を守れば、ある程度読みやすいプログラムになると思う。