LoginSignup
8
14

More than 3 years have passed since last update.

【ExcelVBA】DictionaryのItemにユーザー定義型を指定できない

Last updated at Posted at 2019-04-29

はじめに

仕事でVBAのコードを書いていた時に、「パブリック オブジェクト モジュールで定義されたユーザー定義型に限り、変数に割り当てることができ、実行時バインディングの関数に渡すことができます。」というエラーメッセージが出ました。
当初は何のエラーなのかさっぱり分かりませんでしたが、「DictionaryのItemにユーザー定義型を指定するとコンパイルエラーになる」ということが分かりました。

compile_error.png

サンプルデータ

"Sheet1"という名前のワークシートに記載されている、以下のデータを使用します。

worksheet.png

サンプルコード

  • 下記のコードを作った後、[実行]もしくは[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についてもクラスモジュールを作って対処するのが早道だと思います。
8
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
14