ByRef は「呼び出し元の変数を直接書き換える」ため便利ですが、初心者が使うと副作用で混乱しやすいです。そこで 安全に使うための設計パターン を整理します。
1. 複数値返却パターン
目的
関数の戻り値は1つしか返せないため、複数の結果を返したいときに ByRef を使う。
例
Sub GetCoordinates(ByRef x As Double, ByRef y As Double)
x = 10
y = 20
End Sub
Sub Test()
Dim a As Double, b As Double
GetCoordinates a, b
Debug.Print a, b ' → 10, 20
End Sub
VBポイント
- 「この引数は結果を返すために使う」と明示する。
- コメントや名前付けで「返却用」であることを分かりやすくする。
2. 参照差し替えパターン
目的
呼び出し元の変数が指すオブジェクトや範囲を、意図的に別のものへ差し替える。
例
Sub MoveRange(ByRef r As Range)
Set r = r.Offset(1, 0) ' 呼び出し元も次の行を指すようになる
End Sub
Sub Test()
Dim rng As Range
Set rng = Sheet1.Range("A1")
MoveRange rng
rng.Value = "Shifted" ' A2 に書き込まれる
End Sub
VBポイント
- 「参照を差し替える」意図を明確にする。
- コメントで「このSubは参照を変更します」と書いておくと安心。
3. 戻り値と ByRef の役割分担
目的
- 戻り値: 主な結果を返す
- ByRef: 追加の副次的な情報を返す
例
Function Divide(ByVal a As Double, ByVal b As Double, ByRef remainder As Double) As Double
remainder = a Mod b
Divide = a \ b
End Function
Sub Test()
Dim q As Double, r As Double
q = Divide(10, 3, r)
Debug.Print q ' → 3
Debug.Print r ' → 1
End Sub
VBポイント
- 主結果は戻り値で返す → 読みやすい。
- 副次的な値は ByRef → 複数返却が必要な場合に限定。
4. ByRef を「返却専用」にする
目的
呼び出し元の値を変更しないで、結果だけを返す。
例
Sub CalculateStats(ByVal arr() As Double, ByRef avg As Double, ByRef maxVal As Double)
Dim i As Long, sum As Double
sum = 0
maxVal = arr(LBound(arr))
For i = LBound(arr) To UBound(arr)
sum = sum + arr(i)
If arr(i) > maxVal Then maxVal = arr(i)
Next
avg = sum / (UBound(arr) - LBound(arr) + 1)
End Sub
VBポイント
- 入力は必ず ByVal。
- 出力専用の引数だけ ByRef。
- 役割を分けることで副作用を防ぐ。
5. 明示的に「参照破棄」を行う場合
目的
呼び出し元の参照を意図的に消す(例: リソース解放)。
例
Sub ReleaseRange(ByRef r As Range)
Set r = Nothing ' 呼び出し元も参照が消える
End Sub
VBポイント
- 「このSubを呼ぶと参照が消える」と明示する。
- コメント必須。誤用すると Null 参照エラーになる。
まとめ指針
- 基本は ByVal。
- ByRef は「返却用」「参照差し替え用」に限定。
- コメントや名前付けで意図を明示(例:
ByRef resultX As Double)。 - 戻り値と ByRef を使い分ける → 主結果は戻り値、副次結果は ByRef。
- 参照破棄や差し替えは危険なので必ず明示的に設計。
