Excelが大好きだ!

Excel大好き経理マンがExcelの事を書き綴っていきます。


スポンサードリンク

Excel VBAのオブジェクト型変数の生成から破棄まで 1.勘違い編

以前にこのようなツイをしたところ、諸先輩方から色々なアドバイスを頂きました。

下記ツイのツリーにも記載しましたが、改めて自分がモヤモヤと勘違いしていた点と、教えて頂いてこうだろうと思って点をまとめてみたいと思います。


ひょっとすると私と同じ様な勘違いをしている人もいるかも知れません。
今回は当初私の頭の中にあった図を紹介いたします。
当初の私の勘違いですので明らかに間違っていますのでご注意ください

オブジェクト変数での疑問点

下記は「Excel VBA 実践のための技術」からの抜粋です。

Public Sub Add(ByVal vID As String, _
               ByVal vName As String, _
               ByVal vAge As Long, _
               ByVal vMobile As String)
     
     Dim vStudent As Student  'Studentはユーザー定義クラス
     
     mItemDictionary.Add Key:=vID, item:=mItems.Count + 1
     Set vStudent = New Student
'↓↓↓↓↓ここから
     mItems.Add vStudent      'Collection.addです
     
     With vStudent
          .ID = vID
          .Name = vName
          .Age = vAge
          .Mobile = vMobile
     End With
     Set vStudent = Nothing

'↑↑↑↑↑ここまで
End Sub

疑問点は上記コード内の「ここから」~「ここまで」のコードです。

Collectionに追加したStudentクラスの中身が更新されて(with句内の処理)、
Studentクラスを破棄した時(set vStudent = nothing)に、
Collection内のStudentクラスが影響を受けない事がわからない。


[勘違い版]検証用コード

ここから上記コードをどのように勘違いしていたからわからなかったのか記載いたします。

検証用にもう少し簡略化して全体を見れるコードを用意しました。

Studentクラス

'### Studentクラス
Private mName As String

Public Property Let Name(ByVal vName As String)
     mName = vName
End Property

Public Property Get Name() As String
     Name = mName
End Property



標準モジュール

Sub オブジェクト参照テスト()
     Dim 名簿Col As Collection
     Set 名簿Col = New Collection
     Dim 生徒A As Student
     Set 生徒A = New Student
     
     名簿Col.Add 生徒A
     生徒A.Name = "Kou"
     Debug.Print 名簿Col(1).Name
     
     Set 生徒A = Nothing
     Debug.Print 名簿Col(1).Name
End Sub

上記のコードを実行するとイミディエイトウィンドウに「Kou」が2回表示されます。
2回目は直前で生徒Aを破棄しているのになぜ「Kou」と表示されるのか。


[勘違い版]オブジェクト型変数の生成

f:id:ExcelLover:20211123123434j:plain

「set 生徒A = New Student」というコードで「生徒A」という名前の新しいStudentオブジェクトが作成される。
※Studentはユーザー定義クラスです


[勘違い版]オブジェクト変数を渡す

VBAの変数の渡し方にはByVal型とByRef型があります。

  • ByVal型 (値渡し) ・・・変数の「値」を渡す
  • ByRef型 (参照渡し)・・・変数そのものを渡す

値渡し的イメージ

f:id:ExcelLover:20211123130822j:plain
このコードを実行すると「Kou」とイミディエイトウィンドウに表示されます。

値渡し的なイメージでオブジェクト変数を渡す、つまり変数をコピーして新しい変数がCollectionオブジェクト内に生成されるはず。

そうすると変数「生徒A」とCollectionオブジェクト内の「生徒A②」は別々のオブジェクト変数のはず。
なのに「生徒A.Name = "Kou"」でCollection内の「生徒A②」のNameが変更されているのがおかしい。

f:id:ExcelLover:20211123130946j:plain
「set 生徒A = Nothing」で生徒Aが破棄されても、それぞれ別のオブジェクト変数として存在してるので、Collection内の生徒A②が残っていても違和感はない。


参照渡し的イメージ

f:id:ExcelLover:20211123131457j:plain
参照渡し的イメージでオブジェクト変数を渡す、つまり生徒A②は生徒Aを参照しているはず。
なので「生徒A.Name ="Kou"」でCollection内の「生徒A②」のNameが変更されてもおかしくない。

f:id:ExcelLover:20211123131525j:plain
しかしその後「set 生徒A = Nothing」で生徒Aが破棄されて、生徒A②が破棄されないのはおかしい。


まとめ

自分の知識の中にあった値渡し・参照渡しのいずれの考えた方を用いてもオブジェクト変数の動作を説明することが出来ず悩んでいました。

次回は色々教えて頂いた内容をまとめた(多分ある程度)正しいオブジェクト変数の動作を説明したいと思います。


今回のコードが記載された書籍