エクセル雑感
Variant仮引数のByRefとByValの挙動違い

ExcelマクロVBAとエクセル関数についての私的雑感
最終更新日:2020-07-05

Variant仮引数のByRefとByValの挙動違い


ツイッターで出したVBAのお題です。
Variant型は、どんなデータ型も入れることができてしまいます。
具体的なデータ型の代わりに使用することで、より柔軟にVBA記述ができるようになります。
ただしこの便利さゆえに、逆に注意しなければならない挙動もあります。


お題のツイート

https://twitter.com/yamaoka_ss/status/1279642196430356487

【VBA問題】
Sub sample()
 Dim rng1 As Range, rng2 As Range
 Set rng1 = Range("A1:B1")
 Set rng2 = Range("A2:B2")
 Call sample_sub(rng1, rng2)
End Sub
Sub sample_sub(ByRef v1, ByVal v2)
 v1 = 1
 v2 = 1
 MsgBox WorksheetFunction.Sum(v1, v2)
End Sub
sampleの実行結果は?
・1

・2
・3
・4

VBA マクロ Variant ByRef ByVal 挙動違い

解説のツイート

以下で一連の解説ツイートをしています。

https://twitter.com/yamaoka_ss/status/1279781207975182337


Variantの仮引数に対して実引数でRangeオブジェクトを指定した場合、
ByRef、ByVal、呼ばれた関数ではどちらもRangeオブジェクトとして受け取ります。
しかし、このときByRefとByValでは一部挙動に違いが現れます。
受け取ったRangeオブジェクトに対するVBA記述によって違いが出てきます。


仮引数のVariantでRangeオブジェクトを受け取った場合のByRefとByValでの挙動の違いは、
Set代入した場合には、
ByRefなら参照私として呼び出し元も変化しますが、
ByValなら受け取るだけであり呼び出し元は変化しません。


通常RangeオブジェクトのValueに値を入れる場合は、
Range = 値
このLet代入でRangeが複数セルでも全てのセルのValueに値が入ります。
仮引数がRange型(As Range)であれば、Let代入は同じ動作となります。
しかしVariantでRangeを受け取った場合は、Let代入で挙動に違いが出てきます。



仮引数のVariant変数に対するLet代入では、
ByRefであれば、RangeのValueに値が入りますが、
ByValの場合は、Variant変数のRangeは破棄され、値そのものが変数に入ってしまいます。
これは、プロシージャー内でVariant変数を定義している場合を考えれば当然の結果として理解できます。


Dim v
Set v = Rangeオブジェクト
v.Value = 1 '①
v = 1 '②
①では、Rangeオブジェクトの全てのValueに値(1)が入ります。
②では、変数vに値(1)そのものが入ります。
ByValで受け取った場合は、プロシージャー内で変数定義したことと同じになります。


今回の問題では、
Call sample_sub(rng1, rng2)
Sub sample_sub(ByRef v1, ByVal v2)
v1 = 1 '①
v2 = 1 '②
①では、Rangeオブジェクトの全てのValueに値(1)が入ります。
②では、変数vに値(1)そのものが入ります。


v1は、Range("A1:B1")であり、全てのValueに1が入ります。
v2は、1がVariant/Integerとして代入されます。
結果として、
Sum(Range("A1:B1"), 1)
となるので、答えは3となります。

Variant変数の一般的な説明は、
「VBAのVariant型について」
https://excel-ubara.com/excelvba4/EXCEL_VBA_441.html
Variantデータ型は、他の何らかのデータ型として明示的に宣言されていない変数で、全てのデータ型を入れることができます。Variantデータ型には型宣言文字はありません。Variant型は、特別な値Empty、Error、Nothing、Nullを格納することもできます。




同じテーマ「エクセル雑感」の記事

VBAのString型の最大文字数について
Variantの数値型と文字列型の比較
Variant仮引数にRange.Valueを配列で渡す方法
Variant仮引数のByRefとByValの挙動違い
100桁の正の整数値の足し算
「VBA Match関数の限界」についての誤解
VBAで数値を漢数字に変換する方法
囲碁で相手の石を囲んで取るアルゴリズム
VBAで「3Lと5Lのバケツで4Lの水を作る」を解く
言語依存の関数を使用できるFormulaLocal
配列のUBoundがLBoundがより小さいことはあり得るか


新着記事NEW ・・・新着記事一覧を見る

数式の関数の使用回数、関数名を配列で返す|エクセル雑感(10月19日)
Rangeオブジェクトを受け取り"行数,列数"で埋める|エクセル雑感(10月16日)
数値変数の値を別の変数を使わずに入れ替える|エクセル雑感(10月13日)
WEBスクレイピング(selenium)|Python入門(10月11日)
エクセルを操作する(pywin32:win32com)|Python入門(10月5日)
エクセルを操作する(openpyxl)|Python入門(10月3日)
pipコマンド(外部ライブラリのインストール)|Python入門(10月1日)
CSV読み書き(csvモジュール)|Python入門(9月29日)
「Excel 4.0 マクロ」の使い方|VBA技術解説(9月28日)
CSV読み込みとopen()関数とwith文|Python入門(9月28日)


アクセスランキング ・・・ ランキング一覧を見る

1.最終行の取得(End,Rows.Count)|VBA入門
2.RangeとCellsの使い方|VBA入門
3.変数宣言のDimとデータ型|VBA入門
4.セルのコピー&値の貼り付け(PasteSpecial)|VBA入門
5.マクロって何?VBAって何?|VBA入門
6.Range以外の指定方法(Cells,Rows,Columns)|VBA入門
7.繰り返し処理(For Next)|VBA入門
8.セルに文字を入れるとは(Range,Value)|VBA入門
9.とにかく書いてみよう(Sub,End Sub)|VBA入門
10.マクロはどこに書くの(VBEの起動)|VBA入門




このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。


記述には細心の注意をしたつもりですが、
間違いやご指摘がありましたら、「お問い合わせ」からお知らせいただけると幸いです。
掲載のVBAコードは動作を保証するものではなく、あくまでVBA学習のサンプルとして掲載しています。
掲載のVBAコードは自己責任でご使用ください。万一データ破損等の損害が発生しても責任は負いません。



このサイトがお役に立ちましたら「シェア」「Bookmark」をお願いいたします。
本文下部へ