変数の適用範囲


変数は、宣言のしかたによって、使える場所が限られます。これを、変数の適用範囲といいます。変数の適用範囲を理解するには、VBAの"プロシージャ"と"モジュール"という概念を理解しなければなりません。

プロシージャとモジュール

プロシージャとは、Sub ○○~End SubまたはFunction ○○~End Functionなど、マクロの最小実行単位です。

''2つのSubプロシージャ「Sample1」「Sample2」と
''1つのFunctionプロシージャ「myCalc」がある
Sub Sample1()
    ''何かの処理
End Sub
Sub Sample2
    ''何かの処理
End Sub
Function myCalc(n As Long)
    ''何かの処理
End Function

モジュールとは、こうしたプロシージャを記述する場所です。一般的には、標準モジュールにプロシージャを作成することが多いですね。この標準モジュールは、その名の通りモジュールです。ブックには、標準モジュール以外にも、いくつかのモジュールが用意されています。VBEの[オブジェクトエクスプローラ]に表示される「Sheet1」や「ThisWorkbook」などイベントを記述するモジュールをまとめてDocumentモジュールと呼びます。また、「Sheet1」や「Sheet2」などワークシートのイベントを記述するところをシートモジュール、ブックのイベントを記述する「ThisWorkbook」をブックモジュールと個別に呼ぶこともあります。ほかにも、UserFormを挿入するとFormモジュールが使えるようになります。

プロシージャ内だけで使える変数

今までの解説で登場した変数は、次のようにプロシージャの中で宣言されていました。

Sub Sample15()
    Dim buf As String
    buf = "tanaka"
End Sub

このように、プロシージャの中で宣言した変数は、宣言したプロシージャの中でしか使用できません。

Sub Sample16()
    Dim buf As String
    buf = "tanaka"
End Sub
Sub Sample17()
    MsgBox buf  ''×使用できない(エラーになる)
End Sub

こうした「プロシージャの中で宣言した変数」をプロシージャレベル変数などと呼びます。まぁ、呼び名はどうでもいいです。大事なことはイメージすることです。

すべてのプロシージャで使用できる変数

では、同じ変数を、複数のプロシージャで使用するにはどうしたらいいでしょう。そんなときは、変数をモジュールの宣言セクションで宣言します。宣言セクションとは、モジュールの、最も上に作成したプロシージャよりも、さらに上の部分です。

Dim buf As String
Sub Sample18()
    buf = "tanaka"  ''○使用できる
End Sub
Sub Sample19()
    MsgBox buf  ''○使用できる
End Sub

宣言セクションで宣言した変数は、そのモジュール内のすべてのプロシージャで使用可能です。こうした変数をモジュールレベル変数などと呼びます。まぁ、呼び名はどうでもいいです。大事なことはイメージすることです。

すべてのモジュールで使用できる変数

もう少し視点を広げてみましょう。

いま、標準モジュール「Module1」と標準モジュール「Modukle2」があったとします。Module1の宣言セクションで宣言した変数は、Module1内のすべてのプロシージャで使用できます。しかし、その変数はModule2では使用できません

Module1の宣言セクションで、Dimステートメントを使って宣言した変数は、宣言したModule1内でしか使用できません。もし、宣言したモジュール以外の、別のモジュールでもその変数を使用したいときは、Dimステートメントではなく、Publicステートメントを使って宣言します。

Publicステートメントを使って宣言した変数は、宣言したモジュールはもちろん、すべてのモジュール内の、すべてのプロシージャで使用できます。こうした変数をパブリック変数などと呼びます。まぁ、呼び名はどうでもいいです。大事なことはイメージすること・・・しつこいですか。

適切な適用範囲

変数は、使用する範囲を考えて、適切な方法で宣言してください。パブリック変数はすべてのプロシージャで使えるからといって、何でもかんでもパブリック変数にしてはいけません。たとえば、次のコードをご覧ください。

Sub Sample20()
    Dim i As Long, buf As String
    buf = InputBox("シート名は?")
    For i = 1 To Worksheets.Count
        Worksheets(i).Name = buf & i
    Next i
End Sub
Sub Sample21()
    Dim i As Long, buf As String
    For i = 1 To 10
        buf = Cells(i, 1)
        If Left(buf, 1) = "A" Then
            Cells(i, 3) = "OK"
        End If
    Next i
End Sub

上の2つのプロシージャでは、どちらも変数iと変数bufを使っていますが、それぞれ関連しない、別の処理をするプロシージャです。両方のプロシージャで同じ「Dim i As Long, buf As String」という宣言をするからといって、これを

Dim i As Long, buf As String
Sub Sample22()
    buf = InputBox("シート名は?")
    For i = 1 To Worksheets.Count
        Worksheets(i).Name = buf & i
    Next i
End Sub
Sub Sample23()
    For i = 1 To 10
        buf = Cells(i, 1)
        If Left(buf, 1) = "A" Then
            Cells(i, 3) = "OK"
        End If
    Next i
End Sub

のように、2つの変数をモジュールレベル変数で宣言してはいけません。これは、重大なバグを生む原因になります。モジュールレベル変数や、パブリック変数は、"楽をする"ためにある仕組みではありません。複数のプロシージャ、または複数のモジュール間で、変数を共有しなければならないケースで使うべきものです。