こんにちは。
派犬事務員のコロ子です。
クラスのインスタンスの生成でちょっと気になる事がある。
ループをしながらDictionaryオブジェクト(Collection オブジェクトでも)のアイテムにクラス型のオブジェクトを追加する場合、ループの中でインスタンスを生成しているコードをよく見かける。
【例】
クラスモジュール:Class1
Public data As Long
'標準モジュール
Sub Test1()
Dim dic As Dictionary
Set dic = New Dictionary
Dim i As Long
For i = 1 To 3
Dim dicitem As Class1
Set dicitem = New Class1
Dim dickey As String
dickey = "キー" & i
dicitem.data = i * 2
dic.Add dickey, dicitem
Next i
Dim j As Variant
For Each j In dic.Keys
Debug.Print dic.Item(j).data
Next j
End Sub
イミディエイトウインドウ
2 4 6
結果は正しいけど、なんか納得できない。
なんでループの中でインスタンスを生成しているのだろう?
次のコードTest2、Test3を見ると
Sub Test2()
Dim dic As Dictionary
Set dic = New Dictionary
Dim dicitem As Long
Dim i As Long
For i = 1 To 3
Dim dickey As String
dickey = "キー" & i
dicitem = i * 2
dic.Add dickey, dicitem
Next i
Dim j As Variant
For Each j In dic.Keys
Debug.Print dic.Item(j)
Next j
End Subイミディエイトウインドウ
2 4 6
test2で、DictionaryオブジェクトのアイテムにLong型の変数を入れると値渡ししているように思える。
Sub Test3()
Dim dic As Dictionary
Set dic = New Dictionary
'先にインスタンスを生成する
Dim dicitem As Class1
Set dicitem = New Class1
Dim i As Long
For i = 1 To 3
Dim dickey As String
dickey = "キー" & i
dicitem.data = i * 2
dic.Add dickey, dicitem
Next i
Dim j As Variant
For Each j In dic.Keys
Debug.Print dic.Item(j).data
Next j
End Subイミディエイトウインドウ
6 6 6
でもtest3では参照渡ししているように思える。
ということはtest1では同じ変数名で何度もインスタンスを生成しているということなの??
混乱。
仕方ないのでノンプロ研で質問してみたところ、コロ子、インスタンスの生成について分かっていなかったことが判明。
【分かった事】
①オブジェクト型の変数には参照先が格納される。
なのでTest3ではDictionaryオブジェクトのキー1~3のアイテムには同じインスタンスが格納されている。
②New クラス名でいくつでもインスタンスを生成できる。
Dim 変数名 As クラス名
Set 変数名 = New クラス名
はセットで1変数1インスタンスだと思っていた。
「New クラス名でインスタンスを生成できる」は耳にタコができるほど聞いていたのに・・・。
Dim 変数名 As クラス名 でクラス型の変数を宣言
Set 変数名 = New クラス名 で生成したインスタンスを変数に格納するという意味だった!
Dim A As Class1 Set A = New Class1 Set A = New Class1
と書いたら、2行目のAと3行目のAには別のインスタンスが格納されている。
変数宣言はループ中に書いたら1度しか処理されないのと同じで、
Set 変数名 = New クラス名も1度しか処理されないのかと思っていた!
ちなみに次のように書くとクラス型変数の宣言が不要。
'--省略--
Dim dicKey As String
For i = 1 To 3
dicKey = "キー" & i
dic.Add dicKey, New Class1
dic.Item(dicKey).data = i * 2
Next i
'--省略--そーゆーことなのね!!
ということは、前回の記事で書いたコードはクラス型のオブジェクトを配列にしたりして、普通に分かっている人からしたら意味不明なんだろうな・・・。
クラスモジュール、シートモジュールは前回のまま
標準モジュールのみ書き直し。
Public Sub Aggregate()
Dim list As ListObject
Set list = Sheet1.ListObjects(1)
Dim ran As ListRow
Dim dic As Dictionary
Set dic = New Dictionary
Dim i As Long
'データ読み込み
For Each ran In list.ListRows
Dim dickye As String
dickye = Sheet1.Key(ran)
If dic.Exists(dickye) Then
'すでにkeyが存在している時は、
Call dic.Item(dickye).Add(ran)
Else
'keyが存在しない(初めて)時は、
'Dicにクラス(RawData)型のオブジェクトを格納
dic.Add dickye, New RawData
Call dic.Item(dickye).Init(ran)
i = i + 1
End If
Next ran
Call Sheet2.Del
'データ書き出し
i = 2
Dim j As Variant
For Each j In dic.Keys
Sheet2.Cells(i, cnNo).Value = dic.Item(j).No
Sheet2.Cells(i, cnLot).Value = dic.Item(j).Lot
Sheet2.Cells(i, cnData1).Value = dic.Item(j).data1
Sheet2.Cells(i, cnData2).Value = dic.Item(j).data2
Sheet2.Cells(i, cnData3).Value = dic.Item(j).Data3
i = i + 1
Next j
Call Sheet2.MakeStyle
Sheet2.Activate
End Subインスタンスの生成、完全に理解した!(←こーゆーのが一番怪しい)