こんにちは!Excel VBAの世界へようこそ。
マクロの記録ボタンを押してコードを生成するだけの日々は、もう卒業しましたか?
「ボタンを押すたびに数値をカウントアップしたい」「前回入力したデータを記憶しておいて、次の処理に引き継ぎたい」――そんな野望を抱いたとき、多くの人が最初にぶ-つかるのが「変数の寿命とスコープ(有効範囲)」の壁です。
「あれ? プロシージャが終わったら、さっきまで入っていたデータが消えてしまった……」
そんな絶望を味わった経験はありませんか?
今回は、その悩みを一発で解決する「モジュールレベル変数」と「Static(静的)変数」のスマートな使い分けについて、現場のプロが徹底的に解説します。ここをクリアすれば、あなたのVBAスキルは一段と洗練され、バグ知らずの堅牢なコードが書けるようになりますよ。
—
1. なぜ変数は消えてしまうのか?(基本のおさらい)
まずは、普段私たちが何気なく使っている「ローカル変数」の運命からお話ししましょう。
Sub CountSample()
Dim count As Intger ‘ ローカル変数
count = count + 1
MsgBox “現在のカウントは: ” & count
End Sub
このマクロを実行すると、何回ボタンを押してもメッセージボックスは「現在のカウントは: 1」を表示します。
なぜでしょうか?
VBAのローカル変数(Subプロシージャの中で`Dim`宣言した変数)は、そのプロシージャが実行されている間だけメモリ上に存在します。プロシージャが`End Sub`に到達した瞬間、変数はメモリからきれいさっぱり消去(解放)されるのです。だから、次に実行したときには`count`はリセットされ、また`0`から始まってしまいます。
「じゃあ、いつでもどこからでも触れるように、全部の変数を一番上に書いちゃえばいいや!」
……おっと、それは絶対にやってはいけないアンチパターン(グローバル汚染)です。変数があらゆる場所から書き換え可能になると、バグの温床になり、どこで値がおかしくなったのか追跡不可能なスパゲッティコードが完成してしまいます。
そこで登場するのが、「モジュールレベル変数」と「Static変数」という、美しき状態保持の二大巨頭です。
—
2. モジュールレベル変数:標準モジュールの「一族の共有財産」
モジュールレベル変数は、Subプロシージャの外側、つまり標準モジュールの最上部(Option Explicitの下あたり)で宣言する変数です。
Option Explicit
‘ 【モジュールレベル変数】このモジュール内全体で生き続ける
Private runCounter As Long
Sub ExecuteProcessA()
runCounter = runCounter + 1
MsgBox “プロセスAが実行されました。累計: ” & runCounter & “回”
End Sub
Sub ExecuteProcessB()
runCounter = runCounter + 1
MsgBox “プロセスBが実行されました。累計: ” & runCounter & “回”
End Sub
特徴と使い所
- 寿命: そのExcelファイルを開いている間ずっと(正確には、VBAのリセットボタンが押されるか、エラーで強制終了するまで)。
- スコープ(見える範囲): 宣言したモジュール内。`Private`にすればそのモジュール内からのみアクセス可能。
- 使い所: 「同じモジュール内にある複数のマクロ間で、データを共有・連携させたいとき」に絶大な効果を発揮します。
例えば、処理Aで取得した設定値を、処理Bや処理Cでも使い回したい場合などに最適です。ただし、VBAがリセットされると値が消えるため、「永続的なデータ保存」には向いていません(それはセルの役割です)。
—
3. Static変数:プロシージャの「自分だけの秘密の隠し部屋」
「変数を共有したいわけじゃない。ただ、『ある特定のボタン(マクロ)が押された回数』だけを、そのプロシージャの中に閉じ込めたまま記憶し続けたいんだ!」
そんなピンポイントな要求に対する究極の解答が、`Static`(静的)変数です。
通常の`Dim`の代わりに、`Static`キーワードを使って宣言します。
Sub ClickCounter()
‘ Static変数として宣言
Static totalClicks As Long
totalClicks = totalClicks + 1
MsgBox “このボタンは、今回起動してから ” & totalClicks & ” 回押されました。”
End Sub
奇跡の挙動
このコードを実行してみてください。
ボタンを押すたびに、メッセージボックスの数字が「1、2、3……」と確実に増えていきます。
`End Sub`を通っているにもかかわらず、変数の値が消えていません。
なぜなら、`Static`変数はプロシージャ内に置かれながらも、モジュールレベル変数と同じようにVBAのメモリ領域の永続的なエリアに陣取り、そのプロシージャだけがアクセスできる「専用の隠し部屋」を持っているからです。
特徴と使い所
- 寿命: ファイルを開いている間ずっと保持される。
- スコープ(見える範囲): 宣言したプロシージャの中だけ! 外のプロシージャからは一切見えないし、触れない。
- 使い所: 他のマクロから邪魔されたくない「自分だけの状態(フラグやカウンター)」を保持したいとき。カプセル化(情報の隠蔽)の観点からも、非常に美しい設計ができます。
—
4. 徹底比較:どっちを使うべき? 最適解の選び方
初学者のうちは、「とりあえず全部モジュールレベルにすればいいか」と思いがちですが、それはプログラミングの美学に反します。以下の基準でスマートに使い分けましょう。
| 比較項目 | モジュールレベル変数 (`Private`) | Static変数 (`Static`) |
| :— | :— | :— |
| 宣言する場所 | プロシージャの外(モジュールの最上部) | プロシージャの中 |
| アクセス範囲 | 同一モジュール内のすべてのプロシージャ | 宣言したプロシージャの中だけ |
| 適したシチュエーション | 複数のマクロ間でデータを連携・共有したいとき | 1つのマクロの中で、前回の状態や回数を記憶したいとき |
| カプセル化の度合い | 中(モジュール単位) | 高(プロシージャ単位で完璧に隠蔽) |
黄金律:迷ったら「スコープを狭くする」
プログラミングの世界には「最小権限の原則」という鉄則があります。「変数は、必要最小限の範囲からしか見えないようにするべき」という教えです。
したがって、
1. まずはプロシージャ内で完結する `Static`変数 が使えないかを考える。
2. 複数のプロシージャ間でどうしても共有が必要な場合のみ、モジュールレベル変数を採用する。
3. 絶対にグローバル変数(`Public`変数)には頼らない。
この順番を意識するだけで、あなたの書くVBAコードの品質はプロフェッショナルな領域へと飛躍します。
—
5. 現場のエンジニアが教える「陥りやすい罠」と回避策
最後に、状態保持型変数を使う上で、実務で絶対に知っておくべき「注意点」を伝授します。
罠1:VBAの「リセット」で値は消える
モジュールレベル変数も、Static変数も、あくまで「メモリ上の儚い記憶」です。
以下のアクションが行われると、保持していた値はすべて消失(初期化)します。
- エラーが発生して「デバッグ」モードになったあと、[■(リセット)]ボタンを押した。
- コードを書き換えてコンパイルし直した。
- `End` ステートメントが実行された。
【対策】
「絶対に消えては困るデータ」は、変数に頼らず、非表示のワークシートのセルや、環境変数、テキストファイルなどに書き出す設計にしましょう。変数での状態保持は、あくまで「実行中のセッション内での一時的な効率化」と割り切るのがプロの作法です。
—
まとめ:状態を制する者は、VBAを制す
今回は、モジュールレベル変数とStatic変数の本質と使い分けについて解説しました。
- 変数の寿命とスコープを正しく理解する。
- 複数のプロシージャで共有するならモジュールレベル変数。
- 1つのプロシージャ内でこっそり記憶させたいなら `Static`変数。
- 「最小権限の原則」に従い、スコープはできる限り狭く保つ。
ここをクリアすれば、場当たり的なマクロの記述から脱却し、意図した通りに美しくエレガントに動くシステムを構築できるようになります。
「ここをクリアすれば、Excel VBAの基本はバッチリですよ!」
自信を持って、次の自動化の扉を開いていきましょう。あなたのVBAライフが、より一層素晴らしいものになることを応援しています!
