はじめに
JavaScriptではオブジェクトは参照渡しで代入されるそうです。
(以前の記事)【Vue.js】オブジェクトを代入するときの注意点
VBAではどうなのか調べてみました。
Collectionオブジェクト
Sub Test()
Dim obj1 As Collection
Set obj1 = New Collection
obj1.Add "item1"
Dim obj2 As Collection
Set obj2 = obj1
obj2.Add "item2"
Debug.Print obj1.Count, obj2.Count
'=> 2 2
End Sub
obj2
にitem2
を追加したところ、obj1
も要素数が2個になりました。
オブジェクトは参照渡しで代入されるということですね。
プロシージャの引数について調べてみます。
Sub Test()
Dim obj1 As Collection
Set obj1 = New Collection
obj1.Add "item1"
Dim obj2 As Collection
Set obj2 = obj1
obj2.Add "item2"
Debug.Print obj1.Count, obj2.Count
'=> 2 2
'続き
Call Test2(obj2)
Debug.Print obj1.Count, obj2.Count
'=> 3 3
End Sub
Sub Test2(ByVal obj As Collection)
obj.Add "item3"
End Sub
test2
プロシージャ実行後、obj2
の要素数が3になったので、ByVal
キーワードを使用しても参照渡しになることがわかりました。
(たぶん、オブジェクトのアドレスが値渡しされているのだと思います)
本題とは関係ないですが・・・
Sub Test()
Dim obj1 As Collection
Set obj1 = New Collection
obj1.Add "item1"
Dim obj2 As Collection
Set obj2 = obj1
Set obj1 = Nothing
Debug.Print obj2.Count
'=> 1
End Sub
Set obj1 = Nothing
としてもobj2
は生き残っています。
オブジェクト変数obj1
による、オブジェクトへの参照が破棄されただけで、オブジェクト自体が削除されたわけではないということですね。
Rangeオブジェクト
Sub Test()
Dim rng1 As Range
Set rng1 = Range("A1")
rng1.Value = 123
Dim rng2 As Range
Set rng2 = rng1
rng1.Value = "abc"
Debug.Print rng1.Value, rng2.Value
'=> abc abc
End Sub
こう書くと、オブジェクトは参照渡しで代入されることが一目瞭然ですね。
代入すると新しいオブジェクトができるわけではなく、同じオブジェクトを別の変数がそれぞれ参照しているだけなんですね。
配列
配列について試してみたところ、本題と関係ないことが色々わかりました。
Sub Test()
Dim arr1(0) As Long
arr1(0) = 1
Dim arr2(0) As Long
arr2 = arr1
'=> エラー
End Sub
エラーが出ました。
配列に割り当てることができません
配列は代入できないそうです。
Sub Test()
Dim arr1(0) As Long
arr1(0) = 1
Dim arr2 As Variant
arr2 = arr1
arr2(0) = 2
Debug.Print arr1(0), arr2(0)
'=> 1 2
End Sub
Variant
型の変数に配列を代入することができました。
arr2
を変更してもarr1
が変更されなかったので、値渡しのようです。
「配列は値渡し『的に』代入される」といった感じでしょうか。あまり適切な表現ではないかもしれません。
プロシージャの引数について調べてみます。
Sub Test()
Dim arr1(0) As Long
arr1(0) = 1
Dim arr2 As Variant
arr2 = arr1
arr2(0) = 2
Call Test2(arr1)
Debug.Print arr1(0), arr2(0)
'=> 3 2
End Sub
Sub Test2(arr() As Long)
arr(0) = 3
End Sub
配列を引数とした場合、ByVal
キーワードは使用できず、参照渡しになります。
Sub Test()
Dim arr1(0) As Long
arr1(0) = 1
Dim arr2 As Variant
arr2 = arr1
arr2(0) = 2
Call Test2(arr1)
Debug.Print arr1(0), arr2(0)
'=> 1 2
Call Test3(arr1)
Debug.Print arr1(0), arr2(0)
'=> 3 2
End Sub
Sub Test2(ByVal arr As Variant)
arr(0) = 3
End Sub
Sub Test3(ByRef arr As Variant)
arr(0) = 3
End Sub
Varinat
型の引数に配列を渡すこともできます。
こうすると、ByVal
キーワードを使って配列を値渡しで渡すことができます。
Sub Test()
Dim arr1(0) As Long
arr1(0) = 1
Dim arr2 As Variant
arr2 = arr1
arr2(0) = 2
Call Test2(arr1(0))
Debug.Print arr1(0), arr2(0)
'=> 3 2
End Sub
Sub Test2(ParamArray arr() As Variant)
arr(0) = 3
End Sub
ParamArray
キーワードを使うと、参照渡しとなります。
引数はVariant
型にする必要があります。
ちなみに、ParamArray
は、複数の値を受け取ってを個の配列にとりまとめるもので、配列を受け取るわけではありません。
したがって、Call Test2(arr1)
とすると、配列arr1
を要素する新たな配列が作られ、これが引数となります。
おわりに
値渡しと参照渡しを混同したバグが発生したら、原因調査が難しそうです。
そうならないためにも、これらをきちんと使い分けられるようになりたいです。