Excel VBA | ByValとByRefの違い(値渡しと参照渡し)

VBA
スポンサーリンク

少し抽象的に聞こえる言葉だけど、実際は「中身のコピーを渡すのか」「そのものを渡すのか」という違いです。VBAでは引数の前に ByVal/ByRef を付けて制御します。


基本の考え方

  • ByVal(値渡し):
    引数の「値のコピー」を渡します。中で値を変えても、呼び出し元の変数は変わりません。
  • ByRef(参照渡し):
    変数そのものを渡します。中で値を変えると、呼び出し元の変数も変わります。

動作の違いをコードで理解する

ByValの例(呼び出し元は変わらない)

Sub IncreaseByVal(ByVal n As Integer)
    n = n + 1
End Sub

Sub TestByVal()
    Dim x As Integer
    x = 10
    IncreaseByVal x
    MsgBox x  ' → 10(変わらない)
End Sub
VB

ByRefの例(呼び出し元が変わる)

Sub IncreaseByRef(ByRef n As Integer)
    n = n + 1
End Sub

Sub TestByRef()
    Dim x As Integer
    x = 10
    IncreaseByRef x
    MsgBox x  ' → 11(変更が反映される)
End Sub
VB

型ごとのポイント

  • 数値・文字列:
    ByValで渡すと安全。ByRefは「中で編集して呼び元にも反映したい」ときに使う。
  • 配列:
    VBAの配列は参照型の性質があり、ByRefで渡すと要素変更が呼び元に反映されます。配列の再割り当て(ReDim)やサイズ変更の可否もByVal/ByRefで挙動が変わるため注意。
Sub FillArray(ByRef a() As Integer)
    a(1) = 100  ' 呼び元にも反映
End Sub
VB
  • オブジェクト(Range, Worksheet, Collectionなど):
    参照そのものを扱います。ByValでも「参照のコピー」を渡すので、参照先のプロパティ変更は呼び元に見えます。ただし、参照の付け替えはByRefでないと呼び元に反映されません。
Sub SetValueByVal(ByVal rng As Range)
    rng.Value = "OK"      ' 参照先のセルは更新される
    Set rng = rng.Offset(1, 0) ' 呼び元の参照は変わらない
End Sub

Sub SetValueByRef(ByRef rng As Range)
    rng.Value = "OK"
    Set rng = rng.Offset(1, 0) ' 呼び元の参照もずれる
End Sub
VB

使い分けの指針

  • 安全第一でByVal:
    ラッパー関数やユーティリティ処理では、意図せぬ副作用を避けるために基本はByVal。
  • 複数の結果を返したいならByRef:
    戻り値が1つに限られるFunctionの代わりに、出力用引数をByRefで受け取る。
Sub CalcStats(ByVal a As Integer, ByVal b As Integer, ByRef sum As Integer, ByRef avg As Double)
    sum = a + b
    avg = (a + b) / 2#
End Sub
VB
  • パフォーマンスを気にする場合:
    大きな配列や文字列を大量に渡すときは、ByRefで不要なコピーを避けると高速なことが多い。ただし副作用管理が必須。
  • APIやイベントハンドラ:
    Excel/VBAのイベントや外部API宣言では、仕様に合わせてByVal/ByRefが固定のことがあるので、その通りに書く。

よくある落とし穴と対策

  • 思わぬ副作用:
    ByRefで渡した変数が、プロシージャ内部の変更で書き換わり、バグの原因に。
    対策: 明確に「出力用」「入力用」を分け、出力用だけByRefにする。変数名に役割を示す。
  • オブジェクトの参照の付け替え:
    ByValでRangeを渡すと、Set rng = ... で呼び元の参照は変わらない。
    → 参照自体を更新したいときはByRefを使う。
  • OptionalとByRefの組み合わせ:
    Optionalは既定値が必要で、ByRefでは既定値を設定できないケースがあるため、必要ならVariant + IsMissing適切な既定値のByValを検討。

まとめの一言

  • ByVal: 入力用。中で変えても呼び元は変わらない。安全で予測可能。
  • ByRef: 出力・更新用。中で変えたら呼び元も変わる。強力だが副作用に注意。

迷ったら「基本はByVal、必要なときだけByRef」。それがスッキリしたVBAの第一歩です。

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