/*Google AdSense自動広告*/

2020年5月2日土曜日

Excel VBA ~ ClassをDictionaryに格納するコツ Dictionaryの癖…オブジェクトは参照渡しになってしまう、Watch式に入れるとEmpty Itemができてしまう…

VBAでクラスを使用する人は少ないと思われます(ライブラリとして共用したり、継承したりできないので、そこまでやりたい人は別の言語を使っているのでは…)。
私は結構使っています、シートを設定したら項目でアクセスするものや、作業ログを最後にテキストファイルで吐き出すもの等。
実行速度やオブジェクト指向とか、そういう難しい話でなく、ブラックボックス化して頭を整理するため、というのが本心。

さて、そこで作成したクラスをDictionaryに格納しようとしたところ、おかしな挙動が…。
例えばループで次々にクラスを宣言、格納しても、全て最後に格納したクラスを参照してしまうのです。
どうやら、Dictionaryにクラスなどのオブジェクトを格納する際は、参照渡ししかできないようです。
エッ!!バグですか?仕様ですかそうですか。

通常はオブジェクトと言っても別セルを格納したりすることが多いので、普通に使っている分には問題ないでしょう。
しかしちゃんと新たに宣言したクラスがですよ、VBAにはさっき使ったものと同じに見えるのですよ!
エッ!!バグですか?仕様ですかそうですか。

ということで諦めて配列を使う…にはまだ早い!どうしてもKeyでアクセスしたい場合は、クラスをたくさん作っておけばいいのです。
setArrayを自作クラスとしてmaxNum個作成しておいて、インクリメントしながらDictionary(ここではdics)に格納していく。
配列を使わずに、都度Dimで宣言したりNewで作成した場合、どのKeyでも最後に格納したクラスしか参照できなくなります。
エッ!!バグですか?仕様ですかそうですか。


Dim setArray, dics As New Dictionary, c As Integer

ReDim setArray(maxNum - 1)

Do While Not IsEmpty(setSheet.Cells(r, 1))

    Set setArray(c) = New myClass
    dics.add setSheet.Cells(r, 1).Value, setArray(c) 
    c = c + 1
    r = r + 1
    
Loop

これで、Keyで自作クラスにアクセスできるようになります。Keyを配列の添字に変換するクラスを作ってもいいと思います。

それと、クラスに関係ない話ですが、どうもVBAはオブジェクトを流用(メモリから消えない?)するようで、ループで回す際に、
Dictionaryの前データが邪魔する場合があります。その場合は、新たに宣言したDictionaryでもRemoveAllを入れておくと解決します。

Set units = New Dictionary
units.RemoveAll

また、ウォッチ式が邪魔することがあります。Excelのバージョンによっては、ウォッチ式にDictionaryを入れるとEmptyデータが格納され、ItemのCount=1となってしまうようです。これは実行途中で止めたときにウォッチ式に追加すると回避できます。

まとめ VBAでDictionaryを使う際のTips
  • DictionaryにObjectを格納すると、強制的に参照渡しとなる Object in the Dictionary forces to be reference
  • よって、同名・単一クラスをDictionaryに格納することはできない Therefore, the same name / single class cannot be stored in Dictionary
  • 同名オブジェクトを流用する際は、値が残る場合があるので、RemoveAll等で強制クリアする When diverting an object with the same name, the value may remain, so forcibly clear it with RemoveAll etc.
  • ウォッチ式にDictionaryを入れるとEmptyデータができてしまうので注意する A dictionary to the watch expression, empty data will be created.

0 件のコメント:

コメントを投稿