Excel VBA | 参照渡し(ByRef)

VBA
スポンサーリンク

初心者向けにやさしく、かつ実践的な例を交えて「ByVal(値渡し)と ByRef(参照渡し)」を詳しく説明します。ステップを追って動きを追えるように書くので、手元でコピーして試してみてください(Excel の VBA エディタで動きます)。

まずは概念をかんたんに

  • ByVal(値渡し):変数の「値」だけを渡す。呼び出し先で値を変えても、呼び出し元の変数は変わらない。
  • ByRef(参照渡し):変数そのもの(参照)を渡す。呼び出し先で変えると、呼び出し元にもその変更が反映される。
  • 重要:VBA のプロシージャ(Sub / Function)は、明示しないと デフォルトで ByRef(参照渡し)になります。予期しない副作用を避けるため、明示的に ByVal / ByRef と書くことをおすすめします。

例題 1:ByVal と ByRef の違い(整数で確認)

Sub TestByValByRef()
    Dim a As Integer
    Dim b As Integer

    a = 5
    b = 5

    ChangeValueByVal a
    ChangeValueByRef b

    Debug.Print "After ByVal, a = " & a   ' 出力: After ByVal, a = 5
    Debug.Print "After ByRef, b = " & b   ' 出力: After ByRef, b = 10
End Sub

Sub ChangeValueByVal(ByVal x As Integer)
    x = x + 5
End Sub

Sub ChangeValueByRef(ByRef x As Integer)
    x = x + 5
End Sub
VB

説明:

  • ChangeValueByVal に渡した a は、中の x を変えても a は変わらない(結果:5 のまま)。
  • ChangeValueByRef に渡した b は、x を変えると b も変わる(結果:10)。

手順トレース(簡単)

  1. a = 5b = 5
  2. ChangeValueByVal(a) → 関数内の x は 5 → x = x + 5x = 10(しかし a は 5 のまま)
  3. ChangeValueByRef(b) → 関数内で x を 10 にすると b も 10 に変更される

例題 2:値を「複数」返したいとき(Swap:値の入れ替え)

ByRef を使うと、複数の値(複数の変数)を一度の呼び出しで変更できるので便利です。

Sub TestSwap()
    Dim x As Integer, y As Integer
    x = 3
    y = 7

    SwapValues x, y

    Debug.Print "x = " & x   ' 出力: x = 7
    Debug.Print "y = " & y   ' 出力: y = 3
End Sub

Sub SwapValues(ByRef a As Integer, ByRef b As Integer)
    Dim tmp As Integer
    tmp = a
    a = b
    b = tmp
End Sub
VB

ポイント:abByRef にしているので、呼び出し元の x y が入れ替わります。


例題 3:配列を渡して中身を書き換える(実務でよく使う)

配列やコレクションの要素を変更して呼び出し元に反映させたいとき。

Sub TestArrayModify()
    Dim arr(1 To 3) As String
    arr(1) = "A": arr(2) = "B": arr(3) = "C"

    ModifyArray arr

    Debug.Print arr(1) & "," & arr(2) & "," & arr(3)  ' 出力: X,B,C
End Sub

Sub ModifyArray(ByRef a() As String)
    a(1) = "X"
End Sub
VB

説明:配列の要素を変更すると、呼び出し元の配列にも反映されます。配列は通常参照で扱われるので、変更がそのまま戻ります。

注意点:

  • 配列全体(参照)を別の配列に差し替えたい場合は ReDim などの操作で挙動が変わることがあるので注意。

例題 4:文字列の扱い(ByVal と ByRef の違い)

文字列も同じです。ByVal の場合はコピー渡しになるので、呼び出し元は変わりません。

Sub TestString()
    Dim s As String
    s = "Hello"
    AppendExclamationByVal s
    Debug.Print s  ' 出力: Hello

    AppendExclamationByRef s
    Debug.Print s  ' 出力: Hello!
End Sub

Sub AppendExclamationByVal(ByVal t As String)
    t = t & "!"
End Sub

Sub AppendExclamationByRef(ByRef t As String)
    t = t & "!"
End Sub
VB

よくある混乱ポイント

Q
「プロシージャで ByVal を書き忘れたらどうなる?」
A

VBA はデフォルトで ByRef(参照渡し)です。副作用が欲しくなければ ByVal を明示しましょう。

Q
「ByRef の方が速い?」
A

一概には言えません。小さい値はコピーしてもコストが小さいですが、大きな配列やオブジェクト参照は参照渡しの方が効率的な場合があります。コードの読みやすさと安全性(副作用が問題にならないか)を優先して選びましょう。

Q
「参照渡しでエラーが起きた — なぜ?」
A

呼び出し先で変数の型や構造を変更したり、Nothing を設定したりした場合、呼び出し元で予期せぬ状態になることがあります。特にオブジェクト(例:Range や Worksheet)を渡すときは注意。


初心者向けの実践練習(3問)

問題 1
次のコードの出力は何でしょう?

Sub Q1()
    Dim n As Integer
    n = 2
    MultByTen n
    Debug.Print n
End Sub

Sub MultByTen(ByVal x As Integer)
    x = x * 10
End Sub
VB

→ 答え:2(ByVal のため呼び出し元は変わらない)

問題 2
次のコードの出力は何でしょう?

Sub Q2()
    Dim n As Integer
    n = 2
    MultByTenRef n
    Debug.Print n
End Sub

Sub MultByTenRef(ByRef x As Integer)
    x = x * 10
End Sub
VB

→ 答え:20(ByRef のため呼び出し元も変わる)

問題 3(少し応用)
2つの変数 a=4, b=9 を受け取り、「a を a の 2乗に、b を b の平方根に変更する」プロシージャ Transform を作って呼び出し、呼び出し元の値が変わっていることを確認してください。
→ ヒント:ByRef を使いましょう。

模範解答(参考)

Sub Q3()
    Dim a As Double, b As Double
    a = 4: b = 9
    Transform a, b
    Debug.Print a   ' 16 ?
    Debug.Print b   ' 3 ?
End Sub

Sub Transform(ByRef x As Double, ByRef y As Double)
    x = x ^ 2   ' 4 の 2乗 → 16
    y = Sqr(y)  ' 9 の平方根 → 3
End Sub
VB

おすすめのルール(初心者向け)

  1. 明示的に書くByVal / ByRef はどちらかを必ず明示する(可読性とバグ防止に役立ちます)。
  2. 副作用を避けたいときは ByVal:関数内で元の値を変えてほしくない場合、ByVal にする。
  3. 複数の値を返したいときは ByRef:一度に複数の結果を返したい(返り値が複数欲しい)ときに便利。
  4. オブジェクトや配列の扱いに注意:オブジェクト参照や配列は参照的な振る舞いをするため、期待どおりか確認する。
VBA
スポンサーリンク
シェアする
@lifehackerをフォローする
スポンサーリンク
タイトルとURLをコピーしました