[VBA] Addしていないのに勝手にDictionaryにItemが追加される

  • 投稿日:
  • by
  • Category:

ExcelのVBAを使う際、(C#でいうところの)Listに相当するものとしてDictionaryをよく使う。

古いプログラムでは配列を使っていることもある。もちろん配列でもできることはできるが、項目をAddしたりFor Eachで回したりCountを取ったりをできるだけListに近い感覚で使うならDictionaryだろう。

Collectionでもいいのだが、機能的にもDictionaryのほうが優れていることもあって積極的な理由がない限りはCollectionは使わないことが多い。

この辺は好みなので構わないのだが、実はDictionaryには大きな落とし穴があり今回見事にはまってしまったのでご紹介しておく。

 

 

勝手に項目が追加される問題

Dictionaryを使って開発・デバッグしていると、急に想定しないエラーが出るようになった。

具体的には、Dictionaryを宣言した直後、

 Dim dict As Scripting.Dictionary
Set dict = New Scripting.Dictionary

(↑の段階で)まだ何もAddしていないのにdict.Items(0)が存在している。中身は空。

いやいや何でやねん。

 

何故!?ありえない話し!!

追加していないものが勝手に追加されるなんて普通に考えればあり得ない話。

「バグか?」と思ったがどうもあり得る話のようだ。

原因は一言でいえば、「ウォッチ式にdict.Item(key)を追加していたから」。

確かに直前に実行したときにこのウォッチ式は追加したが、何が悪いというのだろうか。

 

Item(key)をウォッチ式に追加したら何が悪いのか

まず、ItemプロパティはDictionaryオブジェクトのアクセサのようなものだが、実はgetterだけでなくsetterとしても作用する。

Item プロパティ
Dictionary オブジェクト内の指定した_キー_の_アイテム_を設定または取得します。 コレクションについては、指定した key に基づいて item を取得します。 値の取得と設定が可能です

Item プロパティ (Dictionary オブジェクト) | Microsoft Docs

つまりどういうことかといえば、dict.Item("AA")でアクセスした際、

  • dict内にKey:"AA"が存在していれば(当然ながら)Key:"AA"に対応するItemの内容を返してくれるが、
  • dict内にKey:"AA"が存在していない場合、dictに新たな項目(Key:"AA"、Item:"")を作成する

ということらしいのだ。

注釈
item を変更するときに key がない場合は、指定した newitem で新しい key が作成されます。 既存の項目を返そうとしたときに key がない場合は、新しい key が作成され、対応する項目は空のままになります。

Item プロパティ (Dictionary オブジェクト) | Microsoft Docs

ウォッチ式にdict.Item("AA")がずっと残っていたことにより、VBAコードを実行するたびにウォッチ式を表示するためdict.Item("AA")へアクセスが発生する。

でもまだdictは空なのでKey:"AA"は見つからず、結果として空のItem("AA")が追加されてしまっていた、ということになる。

何でこんな仕様にしたの?なんで?

 

クソデカ落とし穴マジでよして

特にVBAは常に開発で使うものではなく、時々出てくる(しかもしばしば重要度が低い)ちょっとしたものの開発に使われるため知識のアップデートが起きにくい。

しかし古いやり方しか知らないと、Excelマクロにありがちな「動くには動くけど遅すぎる」などの問題にぶち当たり、新しい方法を模索せざるを得なくなる。

そして必要に駆られて慣れないやり方を採用するとこういう落とし穴にはまるわけだ。南無三。

VBAを触る際は「当然こう動くはずだろう」を捨てないといけないのだが、まさかこのレベルで注意が必要だとは思わなかった。まだまだ精進が足りない。

コメントする