コロ子勘違いしていた インスタンスの生成
こんにちは。
派犬事務員のコロ子です。
クラスのインスタンスの生成でちょっと気になる事がある。
ループをしながら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
インスタンスの生成、完全に理解した!(←こーゆーのが一番怪しい)