はじめに
仕事でVBAのコードを書いていた時に、「パブリック オブジェクト モジュールで定義されたユーザー定義型に限り、変数に割り当てることができ、実行時バインディングの関数に渡すことができます。」というエラーメッセージが出ました。
当初は何のエラーなのかさっぱり分かりませんでしたが、「DictionaryのItemにユーザー定義型を指定するとコンパイルエラーになる」ということが分かりました。
サンプルデータ
"Sheet1"という名前のワークシートに記載されている、以下のデータを使用します。
サンプルコード
- 下記のコードを作った後、[実行]もしくは[VBAプロジェクトのコンパイル]を行うと、コンパイルエラーが発生します。
- こちらのページによると、「Dictionaryに格納できる値は、Variant型に格納可能なデータのみです。ユーザー定義型は、レジストリに登録されたパブリックな型のみ格納できます。」ということです。
- つまり、このコンパイルエラーは仕様通りの動きだそうです。
Module1
Option Explicit
' ユーザー定義型(メンバー型)
Public Type member
Id As Integer ' ID
Name As String ' 名前
Age As Integer ' 年齢
Address As String ' 住所
End Type
' 「キーがID、値がメンバー型」のDictionaryを作る
Public Sub Test()
Dim membersDic As Object
Set membersDic = CreateObject("Scripting.Dictionary")
With ThisWorkbook.Worksheets("Sheet1")
' Sheet1のデータの入っているRangeを二次元配列として取得する。
Dim rng As Variant: rng = .UsedRange
' ヘッダ行を飛ばして、2行目から取り込む。
Dim i As Integer
For i = 2 To UBound(rng, 1)
Dim Member As Member
Member.Id = rng(i, 1)
Member.Name = rng(i, 2)
Member.Age = rng(i, 3)
Member.Address = rng(i, 4)
membersDic.Add Member.Id, Member ' ★ここでコンパイルエラーが発生します。
Next i
End With
End Sub
回避策
- 以下のようにクラスモジュール(MyMember)を作り、そのクラスをDictionaryのItemにセットすることで、ほぼ同じ処理を実現することができました。
MyMember
Option Explicit
' メンバ変数
Public Id As Integer ' ID
Public Name As String ' 名前
Public Age As Integer ' 年齢
Public Address As String ' 住所
Module1
Public Sub Test()
Dim membersDic As Object
Set membersDic = CreateObject("Scripting.Dictionary")
With ThisWorkbook.Worksheets("Sheet1")
' Sheet1のデータの入っているRangeを二次元配列として取得する。
Dim rng As Variant: rng = .UsedRange
' ヘッダ行を飛ばして、2行目から取り込む。
Dim i As Integer
For i = 2 To UBound(rng, 1)
Dim m As MyMember
Set m = New MyMember
m.Id = rng(i, 1)
m.Name = rng(i, 2)
m.Age = rng(i, 3)
m.Address = rng(i, 4)
membersDic.Add m.Id, m
Next i
End With
End Sub
追記
- Collectionの要素としてユーザー定義型を指定すると、Dictionaryと同様に「パブリック オブジェクト モジュールで定義されたユーザー定義型に限り、変数に割り当てることができ、実行時バインディングの関数に渡すことができます。」というエラーメッセージが出ることが分かりました。
- そのため、Collectionについてもクラスモジュールを作って対処するのが早道だと思います。