【5分でVBA学習】変数の有効範囲「スコープ」（おまけに猫とアクセス修飾子も添えて） ～Excelフィルタリングツール コード解説 #5～

# 1. はじめに
ＡＲアドバンストテクノロジ株式会社（ＡＲＩ）の鈴木タクヤです。

猫って、いいですよね（唐突）。
最近帰省して実家の猫と触れ合ってきました。無理矢理だっこして引っ掻かれたりもしたけれど、私はげんきです。

さて、今回はExcelフィルタリングツールのソースコード解説第5回です。

（ツールの詳細については以下の記事を、「Excelフィルタリングツール解説シリーズ」タグの記事一覧については以下の記事下部「5. 解説記事のリンク」をご参照ください。）

https://qiita.com/TakuyaSuzuki/items/75789a1cf0b3a7c31c0b

__[解説第4回](https://qiita.com/TakuyaSuzuki/items/4402237239e338368257 "【5分でVBA学習】変数の使い方（おまけに定数も添えて） ～Excelフィルタリングツール コード解説 #4～")__ は変数についての記事でしたが、今回のテーマはその有効範囲「スコープ」です。

# 2. 変数の「スコープ」とは
変数は一度宣言しておけば使いたいときに使えて便利ですが、どこでも使えるわけではありません。変数の有効範囲を __スコープ__ と呼びます。

## 2-1 変数のスコープの例

以下はメッセージボックスで変数の内容を表示する例です。
```vb:例①
'Subプロシージャa1
Sub a1()
    Dim x As String                 'String型（文字列）用の変数 x を宣言
    x = "猫って、いいですよね。"     '変数 x に文字列を代入
    MsgBox x                        '変数 x の内容をメッセージボックスに表示する
End Sub
```
例①では __Subプロシージャ[^1]__ `a1`の中で変数 `x`に値を設定し、`x`を __引数[^2]__ としてMsgBox関数を呼んでいます。この`a1`を実行すると・・・
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2566746/a88bfe72-931e-483b-b059-d637f4d5d6cc.png)
↑こうなります。変数 `x`に文字列`"猫って、いいですよね。"`が代入されたことが分かります。
[^1]:Subプロシージャ：「Sub」と「End Sub」で挟まれた、マクロの処理内容を記述する部分のこと。__[解説第2回参照](https://qiita.com/TakuyaSuzuki/items/cbb8f2e32cc0294f53fe "【5分でVBA学習】避けては通れないSubプロシージャ ～Excelフィルタリングツール コード解説 #2～")__。
[^2]:引数：プログラムの中で処理を呼び出す際に渡す値のこと。__[解説第3回参照](https://qiita.com/TakuyaSuzuki/items/1fc0a6c9411e585c8f64 "【5分でVBA学習】使えると便利なFunctionプロシージャ ～Excelフィルタリングツール コード解説 #3～")__。

次は少し変えてみます。
```vb:例②
'Subプロシージャa2
Sub a2()
    Dim x As String                 'String型（文字列）用の変数 x を宣言
    x = "猫って、いいですよね。"     '変数 x に文字列を代入
    Call b2                          'プロシージャ b2 を呼ぶ
End Sub

'Subプロシージャb2
Sub b2()
    MsgBox x                        '変数 x の内容をメッセージボックスに表示する
End Sub
```
例②では2つのSubプロシージャが登場しますが、プロシージャ`a2`は変数`x`に値を設定し、プロシージャ`b2`を呼びます。呼ばれた`b2`は`x`を引数としてMsgBox関数を呼んでいます。この`a2`を実行すると・・・
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2566746/3885f5ac-4ee6-6d83-9aae-f8fce54335fc.png)
↑こうなります。`"猫って、いいですよね。"`が表示されません。

実は __プロシージャ内で宣言した変数は、そのプロシージャ内でしか使用できません。__ このとき __「変数のスコープは宣言されたプロシージャ内のみ」__ と言えます。

例②では`b2`の中で変数`x`を宣言・代入していないため、MsgBox関数で値を表示しようとしている変数`x`は、`"猫って、いいですよね。"`が代入された`x`とは別の変数とみなされてしまいます。よってメッセージボックスは空（Empty）の表示になります。

::: note
スコープから少しでも外れると使えなくなる感じ、飼い猫が家の外で飼い主に呼ばれても知らんぷりするのと似ています（？）
:::

あるプロシージャで宣言した変数の値を別のプロシージャに受け渡したい場合には、以下の例③のように引数として渡す必要があります。
```vb:例③
'Subプロシージャa3
Sub a3()
    Dim x As String                 'String型（文字列）用の変数 x を宣言
    x = "猫って、いいですよね！"     '変数 x に文字列を代入
    Call b3(x)                      'x を引数としてプロシージャ b3 を呼ぶ
End Sub

'Subプロシージャb3
Sub b3(ByVal y As String)           '👈引数をString型で指定
    MsgBox y                        '👈引数として渡された文字列をメッセージボックスに表示する
End Sub
```
（例③の`b3`に登場する`y`は`x`に置き換え可能ですが、`a3`の`x`とは別物であることを強調するために`y`としています。この`y`に`"猫って、いいですよね！"`が渡されます。 ___ByVal___ については __[解説第3回](https://qiita.com/TakuyaSuzuki/items/1fc0a6c9411e585c8f64 "【5分でVBA学習】使えると便利なFunctionプロシージャ ～Excelフィルタリングツール コード解説 #3～")__ をご参照ください。）

この`a3`を実行すると・・・
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2566746/0d643659-f1b9-fc88-c929-0e3f737c420b.png)
↑こうなります。（おわかりいただけただろうか…）

::: note
VBAのコードの先頭に「__Option Explicit__」の記載がある場合には変数の宣言が強制されるため、例②の`a2`を実行すると`b2`側で「変数`x`が宣言されてないよ！」とコンパイルエラー[^3]になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2566746/e7cd40e8-0fa7-b9e7-3320-e21804d30937.png)
:::
https://learn.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/option-explicit-statement

[^3]:コンパイルエラー:人間がプログラミング言語で書いたコードを、0と1のみであらわす機械語に変換する「コンパイル」の際に発生するエラーのこと。

## 2-2 モジュールレベル変数
上では「__プロシージャ内で宣言した変数は、そのプロシージャ内でしか使用できません。__」と書きましたが、最初のSubまたはFunctionより前（「宣言セクション」と呼びます）で宣言した変数は、その __モジュール[^4]__ 全体で使用できます。このような変数を __モジュールレベル変数__ と呼びます。

[^4]:モジュール：VBAのコードを書く場所。複雑な処理の場合には複数のモジュールのプロシージャを組み合わせてマクロの処理を実行することも可能。![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2566746/a63f8841-aea0-3cd0-1206-9ff609a22b62.png)



```vb:例④
Dim x As String    '👈String型（文字列）用の変数 x をモジュールレベル変数として宣言

'Subプロシージャa4
Sub a4()
    x = "猫って、いいですよね！！"     '変数 x に文字列を代入
    Call b4                          'プロシージャ b4 を呼ぶ
End Sub

'Subプロシージャb4
Sub b4()
    MsgBox x                        '変数 x の内容をメッセージボックスに表示する
End Sub
```
例④では`Dim x As String`の記述を`Sub a4()`より前で宣言しています。この`a4`または`b4`を実行すると・・・
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2566746/d1a4cc60-3fa0-3ba4-ff71-98c37e9f8b89.png)
↑こうなります。`x`は`a4`の中で値を代入され`b4`の中で使用されています。つまり`x`のスコープは`a4`と`b4`を含むモジュール全体であることが分かります。

:::note
ちなみにオス猫は複数のメス猫の縄張りを囲むようにして自分の縄張りを決めるとか。モジュールレベル変数のスコープは、オス猫の縄張りみたいなものですね（？）
:::

[Excelフィルタリングツール](https://qiita.com/TakuyaSuzuki/items/75789a1cf0b3a7c31c0b "【ツール提供】Excelユーザなら全員使える?! Excelフィルタリングツール")でも、モジュールレベル変数を使用しています。
```vb:Excelフィルタリングツール冒頭
'このツールの設定値を代入する変数の定義
Dim modeNo As Integer                   'マクロの起動モード設定用（0:表見出しのコピー, 1:フィルタリング, 2:フィルタのクリア）
Dim settingSheetName As String          'フィルタ条件等を記載する本ツールの設定用シート名
（中略）
Dim targetBook As Workbook              'フィルタ対象ブック


'■共通処理（複数のマクロから呼ばれる）
Sub exeCommonProc()                     '👈最初のSub

    '処理高速化のおまじない
    With Applicatio
（後略）
```
最初に登場するSubプロシージャ`exeCommonProc`より前で宣言した変数は、複数のSub/Functionプロシージャで使用しています。（多分。）

:::note warn
予期せぬ変数の値の書き換えバグを防ぐためにも、__変数はなるべく狭いスコープで使用__ することをオススメします。そう、できれば猫の額ほどの。
:::
 
# 3. おわりに
以上、変数のスコープについての解説でした。
悪ノリの猫まみれな記事となりましたが、猫アレルギーの方には申し訳ございません。

次回は、Ifステートメントについて解説します。

👇次回

https://qiita.com/TakuyaSuzuki/items/2bd2c757be640f252b89

# 4. おまけ；アクセス修飾子
[Excelフィルタリングツール](https://qiita.com/TakuyaSuzuki/items/75789a1cf0b3a7c31c0b "【ツール提供】Excelユーザなら全員使える?! Excelフィルタリングツール")では使用していませんが、変数のスコープに関蓮する __アクセス修飾子__ というものがあります。大規模開発などでは必須ですが、個人的なツールの作成などでは必要に迫られないと思うので、アクセス修飾子を2種類だけ紹介します。

:::note
アクセス修飾子は変数以外に使用することもありますが、本記事では変数に絞って紹介します。
:::
## 4-1 Public
モジュールレベル変数の宣言における`Dim`を __`Public`__ に書き換えると、さらにスコープが広がり、他のモジュールでも使用できるようになります。
```vb:
Public x As String

Sub a()
（以下略）
```

## 4-2 Private
モジュールレベル変数の宣言における`Dim`を __`Private`__ に書き換えても、`Dim`と同様にそのモジュール内のどこでも使える変数となります。`Public`とは異なり、他のモジュールでは使用できません。
ただし、`Dim`とは異なり __`Private`__ はプロシージャ内では使用できません。
他のプログラミング言語では`Dim`より __`Private`__ の方がよく使用されます。
```vb:
Private x As String　　　'👈Dim x As String と同じ意味

Sub a()
   Private x As String   '👈プロシージャ内ではコンパイルエラー
End Sub
```

# 5. 参考文献
こちらはMicrosoftの公式ドキュメントです。

https://learn.microsoft.com/ja-jp/office/vba/language/concepts/getting-started/understanding-scope-and-visibility

https://learn.microsoft.com/ja-jp/dotnet/visual-basic/programming-guide/language-features/declared-elements/access-levels

https://learn.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/private-statement

https://learn.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/public-statement

こちらは私がものすごくお世話になっている「エクセルの神髄」という解説サイト。より深く学習を進めたい方は是非。

https://excel-ubara.com/excelvba1/EXCELVBA408.html

猫の縄張りについて。

https://nekogohan-web.jp/nawabari4604/