Excel VBA の引数の渡し方の基本
「値渡し(ByVal)」と「参照渡し(ByRef)」は、プロシージャ(SubやFunction)に変数を渡すときの“渡し方”です。ポイントは「呼び出し先で変えた内容が呼び出し元に戻ってくるかどうか」。初心者はここだけ覚えればOKです。
ことばの意味をかみ砕く
- 値渡し(ByVal)
- イメージ: 変数の“コピー”を渡す。
- 結果: 呼び出し先で値を変えても、元の変数は変わらない。
- 使いどころ: 安全に渡したい、勝手に書き換えられたくないとき。
- 参照渡し(ByRef)
- イメージ: 変数の“本体”を渡す。
- 結果: 呼び出し先で値を変えると、元の変数も変わる。
- 使いどころ: 呼び出し先で結果を作って、そのまま呼び出し元に反映させたいとき。
- 補足(VBAのデフォルト)
- 基本: 引数は指定しないとByRef(参照渡し)になります。変えられたくないなら、明示的にByValを書く癖を付けると安全です。
まずは超シンプルな例
数値の例(ByVal vs ByRef)
Sub TestByVal()
Dim x As Integer
x = 5
Call AddTenByVal(x) ' ← 値渡し
Debug.Print x ' 結果:5(変わらない)
End Sub
Sub AddTenByVal(ByVal n As Integer)
n = n + 10
End Sub
VBSub TestByRef()
Dim x As Integer
x = 5
Call AddTenByRef(x) ' ← 参照渡し
Debug.Print x ' 結果:15(変わる)
End Sub
Sub AddTenByRef(ByRef n As Integer)
n = n + 10
End Sub
VB- ポイント:
- ByVal: 「コピー」を10増やしても、元のxはそのまま。
- ByRef: 「本体」を10増やすので、元のxが書き換わる。
文字列の例と「呼び出し元に返したい」場面
文字列をくっつける(ByRefで結果を返す)
Sub MakeGreeting()
Dim msg As String
msg = "こんにちは"
Call AppendEvening(msg) ' ByRef で結果を“直接”反映
Range("A1").Value = msg ' → 「こんにちは、こんばんは」
End Sub
Sub AppendEvening(ByRef s As String)
s = s & "、こんばんは"
End Sub
VB- ポイント:
- 目的が「結果を作って返す」ならByRefがシンプル。
- 関数の戻り値を使ってもOKだけど、複数の値を返したいときはByRefが便利。
関数での使い分けと安全対策
関数の戻り値を使う(副作用なしでわかりやすい)
Sub UseFunctionSafely()
Dim price As Double
price = 1000
Range("A1").Value = AddTax(price) ' 1100 を表示
Debug.Print price ' 1000(元の値は変わらない)
End Sub
Function AddTax(ByVal amount As Double) As Double
AddTax = amount * 1.1
End Function
VB- ポイント:
- 安全に計算したいなら引数はByVal+戻り値で返す。
- 「元の変数を変えたい」明確な意図があるときだけByRef。
ありがちな落とし穴と回避法
- デフォルトがByRef問題
- 落とし穴: 引数にByValを付け忘れると、意図せず元の変数が書き換わる。
- 回避: 「基本はByVal、書き換えたいときだけByRef」が安全な習慣。
- コレクションやオブジェクト
- 性質: オブジェクト変数(Range、Collectionなど)はByValでも“参照のコピー”が渡るため、内部の状態変更は呼び出し元に影響しがち。
- 回避: 「オブジェクトを丸ごと変更する処理」は慎重に。必要なら新しいオブジェクトを作って返す。
- 読みやすさ(チーム開発でも安心)
- コツ: 「変更を狙う引数はByRef」「それ以外はByVal」とルール化すると、コードの意図が伝わりやすくなる。
練習問題(手で動かして違いを体感)
- 課題1:数値の更新
- やること: 変数xを20にして、ByValで10足すSubと、ByRefで10足すSubをそれぞれ呼び出し、結果の違いをDebug.Printで確認。
- 狙い: ByValは元のxが変わらず、ByRefは変わることを確認。
- 課題2:二つの値を返す
- やること: 引数を二つ(ByRef a, ByRef b)にして、aに「こんにちは」、bに「こんばんは」を入れて返すSubを作る。
- 狙い: 複数の結果を返したいときにByRefが便利なことを体験。
- 課題3:安全な関数設計
- やること: 金額に割引を適用するFunctionを作り、引数はByVal、戻り値で結果を返す。元の金額が変わらないことを確認。
- 狙い: 「副作用なし」の設計感覚を身につける。
解答と解説
練習問題1:数値の更新
問題:
変数 x を 20 にして、ByVal で 10 足す Sub と、ByRef で 10 足す Sub を呼び出し、結果の違いを確認。
解答コード:
Sub TestByVal()
Dim x As Integer
x = 20
AddTenByVal x
Debug.Print x ' 結果:20
End Sub
Sub AddTenByVal(ByVal n As Integer)
n = n + 10
End Sub
Sub TestByRef()
Dim x As Integer
x = 20
AddTenByRef x
Debug.Print x ' 結果:30
End Sub
Sub AddTenByRef(ByRef n As Integer)
n = n + 10
End Sub
VB解説:
- ByVal → コピーを渡すので、呼び出し先で
nを 30 にしても元のxは 20 のまま。 - ByRef → 本体を渡すので、呼び出し先で
nを 30 にすると元のxも 30 に変わる。
練習問題2:二つの値を返す
問題:
引数を二つ(ByRef a, ByRef b)にして、a に「こんにちは」、b に「こんばんは」を入れて返す Sub を作る。
解答コード:
Sub TestGreeting()
Dim msg1 As String, msg2 As String
SetGreeting msg1, msg2
Debug.Print msg1 ' 結果:「こんにちは」
Debug.Print msg2 ' 結果:「こんばんは」
End Sub
Sub SetGreeting(ByRef a As String, ByRef b As String)
a = "こんにちは"
b = "こんばんは"
End Sub
VB解説:
- Sub の中で
aとbに文字列を代入すると、呼び出し元のmsg1とmsg2にそのまま反映される。 - 複数の値を返したいときは ByRef が便利。関数の戻り値は1つしか返せないので、こういう場面で役立つ。
練習問題3:安全な関数設計
問題:
金額に割引を適用する Function を作り、引数は ByVal、戻り値で結果を返す。元の金額が変わらないことを確認。
解答コード:
Sub TestDiscount()
Dim price As Double
price = 1000
Debug.Print ApplyDiscount(price) ' 結果:800
Debug.Print price ' 結果:1000(元の値は変わらない)
End Sub
Function ApplyDiscount(ByVal amount As Double) As Double
ApplyDiscount = amount * 0.8
End Function
VB解説:
- ByVal で渡しているので、呼び出し先で
amountを計算しても元のpriceはそのまま。 - 戻り値で結果を返すので「副作用なし」。安全で読みやすいコードになる。
- 基本は ByVal+戻り値、必要なときだけ ByRef が初心者におすすめの設計。
まとめ
- 練習1: ByVal と ByRef の違いを体感 → 値が変わるかどうか。
- 練習2: 複数の値を返すときは ByRef が便利。
- 練習3: 安全に設計するなら ByVal+戻り値で副作用をなくす。
👉 次のステップとして、「自分の作りたい処理」を考えてみると理解が深まります。例えば「税金計算」「文字列の整形」「セルの値の更新」などを ByVal と ByRef で書き比べてみると、どちらが適しているかが自然に分かってきます。
