0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【VBA】値渡しと参照渡しについて調べてみる

Posted at

はじめに

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

obj2item2を追加したところ、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を要素する新たな配列が作られ、これが引数となります。

おわりに

値渡しと参照渡しを混同したバグが発生したら、原因調査が難しそうです。
そうならないためにも、これらをきちんと使い分けられるようになりたいです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?