最初に押さえるポイントは「渡した値が呼び出し元で変わるかどうか」。
- ByVal(値渡し):中で変えても呼び出し元は変わらない。
- ByRef(参照渡し):中で変えたら呼び出し元も変わる。
直感イメージ
- ByVal: コピーを渡す。コピーをいじっても原本は無傷。
- ByRef: 原本そのものを渡す。いじれば原本が書き換わる。
基本例で体感する
値渡し(ByVal):安全で予測しやすい
Sub Double_ByVal(ByVal n As Long)
n = n * 2
MsgBox "Subの中のn: " & n
End Sub
Sub Test_ByVal()
Dim x As Long: x = 5
Call Double_ByVal(x)
MsgBox "呼び出し元のx: " & x ' 5のまま
End Sub
VB- 結果: Sub内では10になるが、呼び出し元の
xは変わらない。
参照渡し(ByRef):呼び出し元を直接更新
Sub Double_ByRef(ByRef n As Long)
n = n * 2
End Sub
Sub Test_ByRef()
Dim x As Long: x = 5
Call Double_ByRef(x)
MsgBox "呼び出し元のx: " & x ' 10に変わる
End Sub
VB- 結果: Sub内の変更がそのまま呼び出し元へ反映される。
文字列・配列・オブジェクトでも違いは同じ
文字列(String)
Sub Append_ByVal(ByVal s As String)
s = s & "!"
End Sub
Sub Append_ByRef(ByRef s As String)
s = s & "!"
End Sub
Sub Test_String()
Dim msg As String: msg = "Hello"
Append_ByVal(msg) ' msgは"Hello"のまま
Append_ByRef(msg) ' msgは"Hello!"に変わる
End Sub
VB配列(Variant配列の例)
Sub FillArray_ByVal(ByVal arr As Variant)
arr(0) = 100 ' 実はここでエラーになることがある(コピーが固定長扱い等)
End Sub
Sub FillArray_ByRef(ByRef arr As Variant)
arr(0) = 100 ' 呼び出し元の配列が更新される
End Sub
Sub Test_Array()
Dim a(1 To 3) As Long
a(1) = 1: a(2) = 2: a(3) = 3
FillArray_ByRef(a) ' a(1)が100に更新
End Sub
VB- ポイント: 配列は基本的にByRefで扱うと意図通り更新できる。
オブジェクト(Rangeなど)
Sub PaintCell_ByVal(ByVal rng As Range)
rng.Value = "X" ' オブジェクトの中身は更新される
Set rng = rng.Offset(0, 1) ' 参照の付け替えは呼び出し元に影響しない
End Sub
Sub PaintCell_ByRef(ByRef rng As Range)
rng.Value = "X"
Set rng = rng.Offset(0, 1) ' 呼び出し元の変数も右隣セルを指すように変わる
End Sub
VB- 理解のコツ: オブジェクトの「中身の変更」はどちらでも反映されるが、「参照先の付け替え」はByRefでしか呼び出し元に反映されない。
使い分けの指針
- 基本はByVal:
- 安全性: 意図しない書き換えを防ぐ。
- 可読性: そのプロシージャの副作用が少ない。
- ByRefを使うと良い場面:
- 戻り値が1つでは足りない: 複数の結果を引数経由で返したい。
- 大きなデータを直接更新したい: 配列、コレクション、Rangeの再割り当てなど。
- 性能が重要: 巨大構造体をコピーせずに渡したい(VBAでは実務上は配列・Rangeでの意図的更新が主)。
よくある落とし穴と回避策
- 意図せぬ書き換え(ByRefの副作用):
- 対策: 本当に必要な箇所だけByRefにする。名前に意図を込める(例:UpdateScoreByRef)。
- OptionalとByRefの組み合わせの注意:
- 対策: Optionalは基本ByVal+既定値で設計する。ByRefのOptionalは挙動がわかりにくくなる。
- UDF(ワークシート関数)での副作用:
- 対策: セル計算用のFunctionはByValで純粋関数に。更新処理はSubに分離。
- 配列・Rangeの参照付け替えの勘違い:
- 対策: 「中身を変える」と「参照を変える」は別物。参照を変えたいならByRef。
練習問題で定着
練習1:スコアを加点(ByRefで更新)
Sub AddBonus(ByRef score As Long, ByVal bonus As Long)
score = score + bonus
End Sub
Sub Test_AddBonus()
Dim s As Long: s = 70
AddBonus(s, 10) ' sは80になる
End Sub
VB練習2:二重の結果(倍と三倍)を返す
Sub MultiplyBoth(ByVal n As Long, ByRef twice As Long, ByRef thrice As Long)
twice = n * 2
thrice = n * 3
End Sub
Sub Test_MultiplyBoth()
Dim t As Long, th As Long
MultiplyBoth 5, t, th ' t=10, th=15
End Sub
VB練習3:Range参照を右隣へ移す
Sub ShiftRight(ByRef rng As Range)
Set rng = rng.Offset(0, 1)
End Sub
Sub Test_ShiftRight()
Dim r As Range
Set r = Range("A1")
ShiftRight r
r.Value = "Moved" ' B1に書き込まれる
End Sub
VB小さなチェックリスト
- 副作用が必要か: ないならByVal。
- 複数結果が必要か: 必要ならByRefで返す。
- データの種類は何か: 配列・参照の付け替えはByRefが合う。
- 読みやすいか: 更新が起きる引数には動詞や「Out」「Ref」を名前に含めると誤解が減る。
この感覚さえ掴めば、ByValとByRefの選択は「安全に進めるか」「意図通り更新するか」の設計判断になります。まずはByValを基本線に、必要な場面だけByRefを使い分けていきましょう。


