VBAでプロシージャ(Sub / Function)を使っていると、引数の前に記載される ByVal や ByRef を目にすることがあります。しかし実務でプログラムを書く場面では「どちらを使えば良いのか」「違いが曖昧なまま書いている」というケースが多く、引数の扱いを誤ると意図しない値の書き換えやバグの原因につながります。
特に、配列やオブジェクト、Worksheet・Rangeなどを処理する場面では ByRef と ByVal の挙動を正しく理解していないと、コードが予期せぬ動きをすることがあり、トラブルの原因になります。本記事では、値渡しと参照渡しの違いを完全に理解できるよう、実務で頻繁に遭遇するパターンを交えて徹底的に解説します。
目次
- ✅ 【VBA】ByVal と ByRef の基本|どちらも“引数の渡し方”を指定する仕組み
- ・ByVal(値渡し)
- ・ByRef(参照渡し)
- ✅ 【VBA】ByVal と ByRef の違いをコードで理解する
- ・シンプルな数値を使った違いの例
- ・結果の解説
- ✅ 【VBA】配列とオブジェクトにおける ByVal / ByRef の挙動
- ・配列の場合(注意が必要!)
- ・オブジェクト(Workbook, Worksheet, Range など)の場合
- ・ByVal と ByRef の違いが明確に出るケース
- ✅ 【VBA】ByVal を使う場面|意図しない書き換えを防ぐ
- ・1:呼び出し元の値を変更されたくないとき
- ・2:引数として使うだけで、書き換え意図がない場合
- ・3:外部から渡された値が破壊されると困る場合
- ・4:安全なコードを意識したい場合
- ✅ 【VBA】ByRef を使う場面|値の更新が必要な処理
- ・1:呼び出し元の変数に変更を反映したいとき
- ・2:複数の値を返したい場合
- ・複数値を返す例
- ✅ 【VBA】ByVal と ByRef の使い分けを表で比較
- ✅ 【VBA】ByVal / ByRef の実務的な失敗例と対策
- ・よくある失敗:意図せず変数が書き換えられていた
- ・失敗例:オブジェクトの参照が書き換わっていた
- ・失敗例:関数で複数値を返したいが戻り値が1つしかない
- ✅ 【VBA】ByVal と ByRef を使った実務的なサンプル集
- ・サンプル1:値の保護を目的とした ByVal の使用
- ・サンプル2:参照を書き換えたい場合の ByRef
- ・サンプル3:複数の戻り値を渡す実務例
- ✅ 【VBA】ByVal と ByRef を使う際の注意点
- ・1:デフォルトは ByRef
- ・2:配列やオブジェクトの中身は ByVal でも変わる
- ・3:読み取り専用として使う場合は ByVal を選択
- ・4:大規模開発では明示指定が必須
- ✅ 【VBA】RPA(UiPath)との併用時に理解しておくと有利な場面
- ✅ まとめ:ByVal と ByRef の違いを理解して、安全で正確なVBAコードを書こう
✅ 【VBA】ByVal と ByRef の基本|どちらも“引数の渡し方”を指定する仕組み
ByVal と ByRef は、プロシージャに引数を渡すとき 値を渡すか、参照を渡すか を指定するためのキーワードです。
- ByVal(バイバル)=値渡し
- ByRef(バイレフ)=参照渡し
この違いは、処理の結果が呼び出し元に影響するかどうかに直結します。
・ByVal(値渡し)
引数として渡した値を「コピー」して渡す方式。
→ プロシージャ内部で値を変更しても、呼び出し元の変数には影響しない。
・ByRef(参照渡し)
変数そのものを渡す方式。
→ プロシージャ内部で値を変更すると、呼び出し元の変数も変更される。
VBAでは、引数のデフォルトは ByRef のため、明記しないと参照渡しになります。
✅ 【VBA】ByVal と ByRef の違いをコードで理解する
・シンプルな数値を使った違いの例
以下のコードは、プロシージャ内で値を変更したときの挙動を比較する例です。
Sub TestCall()
Dim x As Long
x = 10
Call ChangeVal(x)
Debug.Print x ' ← どうなる?
Call ChangeRef(x)
Debug.Print x ' ← どうなる?
End Sub
Sub ChangeVal(ByVal num As Long)
num = 50
End Sub
Sub ChangeRef(ByRef num As Long)
num = 50
End Sub
・結果の解説
| プロシージャ | 結果 | 理由 |
|---|---|---|
| ChangeVal | x = 10 のまま | 値渡しのため、コピーが変更されただけ |
| ChangeRef | x = 50 に書き換わる | 参照渡しのため、変数そのものが変更された |
この例からもわかる通り、ByVal と ByRef の違いは非常に大きく、意図しない書き換えを防ぐためには正しく選択する必要があります。
✅ 【VBA】配列とオブジェクトにおける ByVal / ByRef の挙動
値型(Long / String など)では挙動が明確ですが、配列やオブジェクトでは話が少し複雑になります。
・配列の場合(注意が必要!)
Sub ChangeArr(ByVal arr As Variant)
arr(1) = 999
End Sub
ByVal で渡していても、arr(1) のように 配列の中身を変更すると、その変更は呼び出し元に反映されます。
これは “配列自身” ではなく “配列の参照” が引き渡されるためです。
配列を完全にコピーしたい場合は、手動でコピーする必要があります。
・オブジェクト(Workbook, Worksheet, Range など)の場合
Sub ChangeSheet(ByVal ws As Worksheet)
ws.Range("A1").Value = "変更"
End Sub
ByVal を付けても、オブジェクトの中身は書き換わります。
理由は配列と同様で、参照そのものは渡されているためです。
・ByVal と ByRef の違いが明確に出るケース
Sub ReplaceObj(ByVal ws As Worksheet)
Set ws = Worksheets("Sheet2")
End Sub
この場合、呼び出し元の変数の参照は変更されません。
しかし、ByRef にすると参照も書き換わります。
Sub ReplaceObjRef(ByRef ws As Worksheet)
Set ws = Worksheets("Sheet2")
End Sub
✅ 【VBA】ByVal を使う場面|意図しない書き換えを防ぐ
ByVal を使うべきなのは、次のような場面です。
・1:呼び出し元の値を変更されたくないとき
例えば、ループ処理で一時的に値を変更したい場合など。
・2:引数として使うだけで、書き換え意図がない場合
意味的に「読み取り専用」としたいときに便利です。
・3:外部から渡された値が破壊されると困る場合
他プロシージャや別モジュールから渡される値の保護目的。
・4:安全なコードを意識したい場合
初心者や複数人開発では副作用を防ぐために ByVal を積極的に使います。
✅ 【VBA】ByRef を使う場面|値の更新が必要な処理
・1:呼び出し元の変数に変更を反映したいとき
代表例は関数の引数。
・2:複数の値を返したい場合
VBAは戻り値が1つしか使えないため、ByRef で複数値を返す方法がよく使われます。
・複数値を返す例
Sub GetSize(ByRef w As Long, ByRef h As Long)
w = 100
h = 50
End Sub
実務で非常に便利なパターンです。
✅ 【VBA】ByVal と ByRef の使い分けを表で比較
| 観点 | ByVal | ByRef |
|---|---|---|
| 値のコピー | 〇(コピー) | ×(コピーしない) |
| 呼び出し元の値を変更できるか | × | 〇 |
| 初期値を守るか | 〇 | × |
| デフォルト設定 | × | 〇 |
| 安全性 | 高い | 低い(意図せぬ変更に注意) |
ByVal は「値の保護」、ByRef は「書き換え目的」という使い方がもっとも自然です。
✅ 【VBA】ByVal / ByRef の実務的な失敗例と対策
・よくある失敗:意図せず変数が書き換えられていた
Call UpdateNumber(x)
引数に何も書かないと ByRef になるため注意が必要 です。
対策
プロシージャ定義で明示的に ByVal を使う。
Sub UpdateNumber(ByVal num As Long)
・失敗例:オブジェクトの参照が書き換わっていた
Call SwitchSheet(ws)
ByRef のままだと、参照が別シートへ変わることがあります。
対策
安全性のため、原則オブジェクトは ByVal を使う。
・失敗例:関数で複数値を返したいが戻り値が1つしかない
対策
ByRef を使用して複数変数に結果を返す。
参考:【VBA】ローカル変数・グローバル変数の違い|スコープを完全理解する徹底ガイド
✅ 【VBA】ByVal と ByRef を使った実務的なサンプル集
・サンプル1:値の保護を目的とした ByVal の使用
Sub CalcDiscount(ByVal price As Long)
price = price * 0.8
End Sub
呼び出し元の price は変更されません。
・サンプル2:参照を書き換えたい場合の ByRef
Sub SetLastSheet(ByRef ws As Worksheet)
Set ws = Worksheets(Worksheets.Count)
End Sub
呼び出し元のシート参照も更新されます。
・サンプル3:複数の戻り値を渡す実務例
Sub GetArea(ByVal width As Double, ByVal height As Double, ByRef area As Double)
area = width * height
End Sub
戻り値が複数必要なときに強力です。
✅ 【VBA】ByVal と ByRef を使う際の注意点
・1:デフォルトは ByRef
意図しない書き換えを防ぐため、明示的な指定を推奨。
・2:配列やオブジェクトの中身は ByVal でも変わる
値型と違う挙動のため、誤解に注意。
・3:読み取り専用として使う場合は ByVal を選択
安全性が高まります。
・4:大規模開発では明示指定が必須
複数人開発では引数の挙動を明確にする必要があります。
✅ 【VBA】RPA(UiPath)との併用時に理解しておくと有利な場面
実務では VBA と RPA(UiPath、Power Automate)を組み合わせるケースも多く、ByVal と ByRef の理解があると以下のメリットがあります。
- Excel マクロが書き換える値の範囲を明確化できる
- RPAから呼び出したVBAで「保持すべき変数」を守れる
- 参照を誤って書き換えてプロセスが止まるトラブルを防止
- 大量ファイル処理の際に意図しない副作用を低減
特に UiPath は Excelインスタンスを保持して動作するため、VBA側で不必要に参照を変更してしまうとフローに影響が出ることがあります。
✅ まとめ:ByVal と ByRef の違いを理解して、安全で正確なVBAコードを書こう
- ByVal は値渡し、ByRef は参照渡し
- 値を守りたい場合は ByVal、書き換えたい場合は ByRef を使う
- VBAでは デフォルトが ByRef のため、明示しないと意図しない書き換えが起こる
- 配列やオブジェクトは ByVal でも中身が変更されるため注意
- 実務では ByVal を基本にし、変更が必要な部分だけ ByRef を用いると安全性が高まる
- RPA(UiPathやPower Automate)と併用する際にも理解しておくとトラブルを回避できる
ByVal と ByRef を正しく理解することで、予期せぬ書き換えを防ぎ、保守性の高いコードが書けるようになります。特にチーム開発や大規模処理では、引数の指定がコード品質を左右する重要なポイントです。