目次
はじめに
関数を呼び出すたびにカウンタを増やしたり、前回の処理結果を記憶させたりしたい場面があります。Staticを使うと、プロシージャ内の変数の値を次回呼び出し時まで保持できるようになります。
Staticとは
Staticは、変数宣言の際に使用するキーワードです。通常の変数は、プロシージャの実行が終わると値が消えてしまいますが、Staticを付けた変数は値を保持し続けます。
具体的には、次のような特徴があります。
- プロシージャの実行が終わっても値が消えない
- 次回そのプロシージャを呼び出したときに、前回の値から処理を続けられる
- プロシージャ内でのみ使用できる(スコープはプロシージャ内に限定される)
モジュールレベル変数(プロシージャの外で宣言する変数)と似ていますが、Static変数はプロシージャ内でしか使えないため、他の処理から誤って値を変更される心配がありません。
基本的な使い方
Static変数は、Dimの代わりにStaticキーワードを使って宣言します。
Static 変数名 As データ型
実際に動作を確認できるコードを用意しました。以下のコードを標準モジュールにコピペして実行してみてください。
Sub CountCalls()
Static callCount As Long
callCount = callCount + 1
Debug.Print "この関数は" & callCount & "回呼ばれました"
End Sub
このコードを何度か実行すると、イミディエイトウィンドウに「この関数は1回呼ばれました」「この関数は2回呼ばれました」と表示されます。callCountの値が保持されているため、呼び出すたびにカウントが増えていくのが確認できます。
【初期値について】
Static変数は、最初にプロシージャが呼ばれたときに自動的に初期化されます。数値型なら0、文字列型なら空文字("")が初期値になります。明示的に初期値を設定したい場合は、最初の呼び出し時だけ判定して値を設定する方法があります。
通常の変数との違い
Dimで宣言した通常の変数とStatic変数の違いを見てみます。
通常の変数を使った場合は、次のようになります。
Sub CountWithDim()
Dim callCount As Long
callCount = callCount + 1
Debug.Print "この関数は" & callCount & "回呼ばれました"
End Sub
このコードを何度実行しても、常に「この関数は1回呼ばれました」と表示されます。プロシージャの実行が終わるたびにcallCountの値が失われ、次回の実行時には0に戻ってしまうためです。
一方、Static変数を使った場合は、値が保持されるため正しくカウントできます。この違いが、Staticを使う最大の理由です。
実用的な活用例
【処理の進捗を記録する】
大量のデータを処理する際に、どこまで処理したかを記録しておくと便利です。
Sub ProcessData()
Static lastRow As Long
Dim ws As Worksheet
Set ws = ActiveSheet
If lastRow = 0 Then lastRow = 1
Dim i As Long
For i = lastRow To lastRow + 99
If i > ws.Cells(ws.Rows.Count, 1).End(xlUp).Row Then Exit For
ws.Cells(i, 2).Value = ws.Cells(i, 1).Value * 2
Next i
lastRow = i
Debug.Print (lastRow - 1) & "行目まで処理しました(次回は" _
& lastRow & "行目から)"
End Sub
このコードでは、lastRowに前回処理した最終行を記録しています。100行ずつ処理するように設定しているため、このプロシージャを何度か実行すれば、続きから処理を再開できます。
【一度だけ実行する初期化処理】
プロシージャ内で一度だけ実行したい初期化処理がある場合にも使えます。
Sub InitializeOnce()
Static isInitialized As Boolean
If Not isInitialized Then
Debug.Print "初期化処理を実行します"
isInitialized = True
End If
Debug.Print "通常の処理を実行します"
End Sub
Boolean型のStatic変数を使って、初期化済みかどうかを判定しています。最初の実行時だけ初期化処理が行われ、2回目以降は通常の処理のみが実行されます。
【前回の値と比較する】
前回の処理結果と比較して、変化があったときだけ処理を行う場合にも便利です。
Sub DetectChange()
Static prevValue As String
Dim currentValue As String
currentValue = Range("A1").Value
If currentValue <> prevValue Then
Debug.Print "値が変更されました: " & prevValue & " → " & currentValue
prevValue = currentValue
Else
Debug.Print "値に変更はありません"
End If
End Sub
A1セルの値を監視して、前回から変化があった場合のみメッセージを表示します。
A1セルの値を書き換えて何度か実行すると、変化の検出を確認できます。
使用時の注意点
Static変数を使う際には、いくつか注意すべき点があります。
【値のリセット方法】
Static変数の値は、Excelを終了するかVBAをリセット(実行中にStopボタンを押す、またはVBエディタで「実行」→「リセット」を選択)するまで保持され続けます。意図的に値をリセットしたい場合は、専用のプロシージャを用意するか、条件を設けて初期化する必要があります。
【スコープの制限】
Static変数はプロシージャ内でのみ有効です。他のプロシージャから値を参照したり変更したりすることはできません。複数のプロシージャで値を共有したい場合は、モジュールレベル変数を使う必要があります。
【デバッグの難しさ】
Static変数は値が保持されるため、プロシージャを何度も実行していると、思わぬタイミングで予想外の値になっている場合があります。デバッグ時には、値をリセットしてから動作確認するのがおすすめです。
【使いすぎに注意】
Static変数は便利ですが、使いすぎるとコードの動作が追いにくくなります。値が保持されることで、実行順序や実行回数に依存した動作になりやすいためです。本当に必要な場面でのみ使用するのが良いです。
以下のような場合は、Static変数が適しています。
- 呼び出し回数をカウントする
- 前回の処理結果と比較する
- 一度だけ実行する初期化処理を制御する
- 処理の進捗を記録する
逆に、単に複数のプロシージャで値を共有したい場合は、モジュールレベル変数やパラメータの受け渡しを検討するのがおすすめです。
まとめ
Staticを使うと、プロシージャ内の変数の値を次回呼び出し時まで保持できます。カウンタや前回値の記録、一度だけ実行する初期化処理など、値を保持したい場面で活用できる便利な機能です。