【Excel VBA入門 第13回】ByValとByRefの違いを理解する ~引数の渡し方を正しく使い分ける~

スポンサーリンク
Excel VBA

イントロダクション

前回(第12回)では、処理結果を戻り値として返せる Function プロシージャの基礎を学んだ。

今回は、SubFunction に共通する重要なテーマ、引数の渡し方について掘り下げていこう。
VBAでは引数を渡す方法として ByValByRef の2種類があり、この違いをきちんと理解していないと、思わぬバグの原因にもなる。

ByValとByRefとは

引数を渡す方法には、次の2種類がある。

  • ByVal(値渡し)
    呼び出し元の変数の「値のコピー」を渡す。プロシージャ内で引数を変更しても、呼び出し元の変数には影響しない
  • ByRef(参照渡し)
    呼び出し元の変数そのもの(メモリ上の場所)を渡す。プロシージャ内で引数を変更すると、呼び出し元の変数の値も変わる

VBAの既定はByRef

ここが初心者がつまずきやすい最大のポイントである。
多くのプログラミング言語では値渡しが基本だが、VBAでは何も指定しないと自動的に ByRef(参照渡し)になる。

' 何も書かなければ ByRef 扱い
Sub サンプル(値 As Long)
    ' ...
End Sub

' ByRef と明示しても同じ意味
Sub サンプル(ByRef 値 As Long)
    ' ...
End Sub

「意図せず呼び出し元の変数を書き換えてしまった」というバグは、この既定動作を知らないことが原因であるケースが多いのである。

実践コードで違いを比較する

実際に、ByValByRef で動作がどう変わるかをコードで見てみよう。
なお、コードは、前回で作成した Module1 に引き続き追記していく。

Sub ByValテスト(ByVal 数値 As Long)
    数値 = 数値 * 2
    Debug.Print "プロシージャ内: " & 数値
End Sub

Sub ByRefテスト(ByRef 数値 As Long)
    数値 = 数値 * 2
    Debug.Print "プロシージャ内: " & 数値
End Sub

Sub 実行してみる()
    Dim A As Long, B As Long
    A = 10
    B = 10

    ByValテスト A
    Debug.Print "呼び出し元のA: " & A   ' → 10(変化なし)

    ByRefテスト B
    Debug.Print "呼び出し元のB: " & B   ' → 20(変化する)
End Sub

実行してみると、ByVal で渡した A はプロシージャ内で2倍にしても呼び出し元では10のままだが、ByRef で渡した B は呼び出し元でも20に変わっていることが確認できる。

実行する際の注意点

上記のコードを実際に動かす際は、以下の点に注意しよう。

イミディエイトウィンドウを表示しておく
Debug.Print の結果は「イミディエイトウィンドウ」に出力されるが、初期状態では非表示になっていることが多い。VBAエディタのメニューから 「表示」→「イミディエイトウィンドウ」(またはショートカット Ctrl + G)で表示しておこう。表示していないと、正常に実行されていても結果が見えず「何も起きていない」ように見えてしまう。

この点さえ押さえておけば、以降の回で登場する Debug.Print を使ったサンプルコードも、同じ要領で結果を確認できる。

どちらを使うべきか

  • ByVal を使うべき場面
    引数を「入力値」として使いたいだけで、呼び出し元の変数を変更したくない場合。基本的にはこちらを既定の選択にするのが安全
  • ByRef を使うべき場面
    プロシージャの中で呼び出し元の変数そのものを書き換えたい場合(例:ステータスを更新する、複数の結果を一つの引数経由で返す、など)

実務では、「意図せぬ副作用」を防ぐために基本は ByVal を明示するのがおすすめだ。
ByRef が必要な場面は限られているため、あえて明示することでコードの意図が読み手にも伝わりやすくなる。

' 意図が明確な書き方
Function 割引後価格(ByVal 価格 As Long, ByVal 割引率 As Double) As Long
    割引後価格 = 価格 * (1 - 割引率)
End Function

まとめ

今回は、引数の渡し方における ByValByRef の違いを学んだ。

  • ByVal は値のコピーを渡す。呼び出し元には影響しない
  • ByRef は変数そのものを渡す。呼び出し元の値も変わる
  • VBAの既定は ByRef なので要注意。基本は ByVal を明示するのが安全

「なんとなく動くコード」から「意図が明確なコード」へ、一歩近づいたのではないだろうか。

次回(第14回)は、引数をさらに柔軟に扱うための「複数引数の並べ方」と、省略可能なOptional引数について解説していこう。

コメント

タイトルとURLをコピーしました