「スコープ」は“見える範囲”という発想でとらえる
VBAでいう「スコープ」は、ざっくり言うと
「この変数(やプロシージャ)は、どこから見える(使える)のかという“範囲”」です。
同じ名前の変数でも、書いた場所によって「ここでは使えるけど、あそこからは見えない」ということが起こります。
この“見える/見えない”のルールを知らないままコードを書くと、「さっきの変数がここで使えない」「別のところで同じ名前を使って壊れた」みたいな混乱が起きやすくなります。
だから、スコープの感覚を早めに持っておくと、
「この変数は、この中だけで完結させよう」
「これは全体で共有したいから、もう少し広い範囲に置こう」
と、設計の考え方が一段クリアになります。
まずは変数のスコープから入る
プロシージャの中だけで使える変数(ローカル変数)
一番よく使うのが、「その Sub / Function の中だけで有効な変数」です。
プロシージャの中で Dim した変数は、そのプロシージャの外からは見えません。
Option Explicit
Sub Sample1()
Dim msg As String
msg = "Sample1 からこんにちは"
MsgBox msg
End Sub
Sub Sample2()
MsgBox msg ' ← ここではエラー(msg が見えない)
End Sub
VBmsg は Sample1 の中で Dim されています。
だから「Sample1 の中では使えるけれど、Sample2 からは存在しない扱い」になります。
ここで感じてほしいのは、
「変数は、宣言した“場所”によって、見える範囲が決まる」
ということです。
超初心者のうちは、
「基本は“その Sub の中だけで使う変数”として Dim する」
というスタイルをベースにすると、スコープで迷子になりにくくなります。
モジュールの先頭で宣言した変数(モジュールレベル変数)
次に、「同じモジュールの中の複数のプロシージャで共有したい変数」を考えます。
その場合は、モジュールの一番上(プロシージャの外)で Dim します。
Option Explicit
Dim counter As Long ' モジュールレベル変数
Sub CountUp1()
counter = counter + 1
MsgBox "CountUp1: " & counter
End Sub
Sub CountUp2()
counter = counter + 1
MsgBox "CountUp2: " & counter
End Sub
VBこの場合、counter は「このモジュールの中にあるすべてのプロシージャから見える」変数になります。
CountUp1 を実行してから CountUp2 を実行すると、counter の値は引き継がれます。
・Sample1 の中で Dim した msg は Sample1 の中だけ
・モジュールの先頭で Dim した counter は、そのモジュールの Sub / Function から共通で見える
この違いが、「プロシージャレベル」と「モジュールレベル」のスコープの違いです。
Public / Private と「プロジェクト全体」のスコープ
Public 変数:ブック全体から見える変数
さらに一歩広げると、「このブックのどのモジュールからでも使える変数」が欲しくなることがあります。
そのときに使うのが Public です。
Option Explicit
Public UserName As String ' プロジェクト全体から見える
Sub SetUserName()
UserName = "山田"
MsgBox "ユーザー名を設定しました"
End Sub
VB別の標準モジュールからでも、UserName にアクセスできます。
Option Explicit
Sub ShowUserName()
MsgBox "現在のユーザーは " & UserName & " さんです"
End Sub
VBここでは、
・UserName は「プロジェクト全体(このブックのすべてのモジュール)」から見える
という、最も広いスコープを持っています。
ただし、超初心者のうちから何でもかんでも Public にしてしまうと、
「どこからでも書き換えられてしまう危険なグローバル変数だらけ」になり、バグの温床になります。
なので、
「本当に全体で共有したいものだけ Public」
という意識を持っておくと、後々かなり楽になります。
Private 変数:モジュールの中だけに閉じ込める
モジュールレベル変数を Dim ではなく Private で宣言すると、
「そのモジュールの中だけで使える変数」という意味がより明確になります。
Option Explicit
Private totalCount As Long ' このモジュールの中だけ
Sub AddCount()
totalCount = totalCount + 1
End Sub
Sub ShowCount()
MsgBox "合計回数: " & totalCount
End Sub
VB別のモジュールから totalCount を参照しようとすると、コンパイルエラーになります。
「この変数は、このモジュールの中だけで完結させたい」という意図を、Private がはっきり表してくれます。
Dim でモジュール先頭に書いた場合も、既定では「そのモジュール内だけ」のスコープですが、
Private を付けると「意図的に閉じている」ことが読み手に伝わるので、設計としても分かりやすくなります。
プロシージャ(Sub / Function)にもスコープがある
Public Sub / Private Sub の違い
スコープの考え方は、変数だけでなくプロシージャにも適用されます。
Option Explicit
Public Sub PublicProc()
MsgBox "どのモジュールからでも呼べる Sub です"
End Sub
Private Sub PrivateProc()
MsgBox "このモジュールの中からしか呼べない Sub です"
End Sub
VBPublicProc は、他のモジュールからも呼び出せます。
Sub CallPublic()
PublicProc ' OK
End Sub
VB一方、PrivateProc は同じモジュールの中からしか呼べません。
別モジュールから呼ぼうとすると、「定義されていません」というエラーになります。
ここでもキーワードは
「どこから見えるようにしたいか」=スコープ
です。
・外部から呼んでほしい窓口的な Sub は Public
・内部でだけ使う“裏方”の Sub は Private
という分け方を意識すると、コードの見通しがかなり良くなります。
スコープを体感するための小さな例題
同じ名前の変数でも“別物”になる例
次のコードを見てください。
Option Explicit
Sub Proc1()
Dim x As Long
x = 10
MsgBox "Proc1 の x = " & x
End Sub
Sub Proc2()
Dim x As Long
x = 20
MsgBox "Proc2 の x = " & x
End Sub
VBどちらも x という名前ですが、
・Proc1 の中の x
・Proc2 の中の x
は、完全に別の変数です。
Proc1 を実行すれば「10」、Proc2 を実行すれば「20」が表示されます。
お互いの x は見えませんし、影響もしません。
ここで感じてほしいのは、
「名前が同じでも、スコープが違えば別物」
ということです。
モジュールレベル変数とローカル変数の違いを感じる
次に、モジュールレベル変数とローカル変数を混ぜた例です。
Option Explicit
Dim count As Long ' モジュールレベル
Sub IncLocal()
Dim count As Long ' ローカル
count = count + 1
MsgBox "IncLocal の count = " & count
End Sub
Sub IncModule()
count = count + 1
MsgBox "IncModule の count = " & count
End Sub
VBIncLocal の中で Dim した count は、その Sub の中だけのローカル変数です。
モジュール先頭の count とは別物です。
・IncLocal を何度実行しても、毎回ローカル変数 count は 0 からスタートして 1 になります。
・IncModule を実行すると、モジュールレベルの count が 1, 2, 3… と増えていきます。
「同じ名前でも、どこで宣言したかによって、どの count を指しているかが変わる」
これがスコープの具体的な“効き方”です。
超初心者向けの実践的なスコープの使い方ルール
最後に、超初心者の段階で意識しておくと楽になる“ゆるいルール”をまとめます。
・まずは「変数は基本、Sub / Function の中で Dim して、その中だけで使う」と決めてしまう。
・どうしても複数のプロシージャで共有したい値が出てきたら、モジュール先頭に Dim(または Private)して「このモジュールの中だけで共有」にとどめる。
・Public で全体共有するのは、「本当に全体から見えてほしいもの」に限る。
このくらいの意識でも、
「どこからでも何でも触れる“野ざらし状態”」からは一気に卒業できます。
スコープは、いきなり完璧に覚える必要はありません。
ただ、「変数やプロシージャには“見える範囲”があるんだ」という感覚だけ持っておくと、
コードを書きながら自然と「ここは閉じておこう」「ここは窓口にしよう」という発想が育っていきます。
