Excel VBA | ByVal と ByRef(値渡し/参照渡し)

VBA
スポンサーリンク

最初に押さえるポイントは「渡した値が呼び出し元で変わるかどうか」。

  • 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を使い分けていきましょう。

VBA
スポンサーリンク
シェアする
@lifehackerをフォローする
スポンサーリンク
タイトルとURLをコピーしました