ExcelVBAでプログラムを組んでいると、「氏名」「年齢」「部署」のように、種類の異なる複数の情報をひとまとめにして扱いたい場面が出てきます。変数をバラバラに宣言することもできますが、関連する情報が多くなるほどコードが読みにくくなってしまいます。
この記事では、複数のデータをひとつの「型」としてまとめられるType…End Type(ユーザー定義型)の使い方を、基本から配列・プロシージャでの活用方法まで解説します。
Type…End Typeとは
Type…End Typeは、名前も型も異なる複数の変数を、ひとつの独自データ型としてまとめて定義できる機能です。他のプログラミング言語でいう「構造体」に近いイメージです。
例えば「社員情報」を扱いたい場合、氏名(文字列)・年齢(数値)・入社日(日付)のように種類が異なるデータを、Dimでバラバラに宣言する代わりに、ひとつの型としてまとめられます。
基本の書き方
' 標準モジュールの先頭(宣言セクション)に記述
Type Employee
Name As String
Age As Integer
HireDate As Date
End Type
Type 型名 から End Type までの間に、まとめたい項目(メンバー)を メンバー名 As データ型 の形で並べます。定義した型は、標準モジュールの他のプロシージャから通常の型(StringやLongなど)と同じように使えます。
変数として使う
定義したユーザー定義型は、Dimで通常の変数と同じように宣言し、.(ドット)でメンバーにアクセスします。
Sub UseEmployeeType()
Dim emp As Employee
emp.Name = "山田太郎"
emp.Age = 32
emp.HireDate = DateSerial(2018, 4, 1)
MsgBox emp.Name & "さん(" & emp.Age & "歳)" & vbCrLf & _
"入社日:" & Format(emp.HireDate, "yyyy/mm/dd")
End Sub
emp.Name、emp.Ageのように、オブジェクトのプロパティと同じ感覚で各項目にアクセスできるのがポイントです。
配列と組み合わせて複数件のデータを扱う
ユーザー定義型は、配列と組み合わせることで真価を発揮します。例えば複数人の社員データを、それぞれ別の配列で管理するとコードが煩雑になりますが、ユーザー定義型の配列にすれば1つの配列でまとめて管理できます。
Sub UseEmployeeArray()
Dim emps(1 To 3) As Employee
Dim i As Long
emps(1).Name = "山田太郎"
emps(1).Age = 32
emps(1).HireDate = DateSerial(2018, 4, 1)
emps(2).Name = "佐藤花子"
emps(2).Age = 27
emps(2).HireDate = DateSerial(2021, 4, 1)
emps(3).Name = "鈴木一郎"
emps(3).Age = 45
emps(3).HireDate = DateSerial(2005, 10, 1)
For i = LBound(emps) To UBound(emps)
Debug.Print emps(i).Name & " / " & emps(i).Age & "歳 / " & _
Format(emps(i).HireDate, "yyyy/mm/dd")
Next i
End Sub
イミディエイトウィンドウに3人分の情報がまとめて出力されます。社員が増えても配列のサイズを変えるだけで対応でき、それぞれの人物ごとにまとまった情報として扱えるため、コードの見通しが良くなります。
セルのデータをユーザー定義型に読み込む
実務では、シート上の表データをユーザー定義型の配列に読み込んで処理することがよくあります。以下は、A列に氏名・B列に年齢・C列に入社日が入力された表を読み込む例です。
Sub LoadEmployeesFromSheet()
Dim ws As Worksheet
Dim emps() As Employee
Dim lastRow As Long
Dim i As Long
Set ws = ThisWorkbook.Worksheets("社員一覧")
lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
ReDim emps(1 To lastRow - 1)
For i = 1 To lastRow - 1
emps(i).Name = ws.Cells(i + 1, "A").Value
emps(i).Age = ws.Cells(i + 1, "B").Value
emps(i).HireDate = ws.Cells(i + 1, "C").Value
Next i
' 30歳以上の社員だけを抽出して表示
For i = LBound(emps) To UBound(emps)
If emps(i).Age >= 30 Then
Debug.Print emps(i).Name & "(" & emps(i).Age & "歳)"
End If
Next i
End Sub
このように、シートから読み込んだ時点で「1行=1レコード」として扱えるため、条件で絞り込んだり並べ替えたりする処理がとても書きやすくなります。
ユーザー定義型をメンバーに持つ入れ子構造
ユーザー定義型の中に、別のユーザー定義型をメンバーとして持たせることも可能です。例えば「住所」を独立した型にして、社員情報の中に含めることができます。
Type Address
PostalCode As String
Prefecture As String
City As String
End Type
Type EmployeeDetail
Name As String
Age As Integer
HomeAddress As Address
End Type
Sub UseNestedType()
Dim emp As EmployeeDetail
emp.Name = "田中次郎"
emp.Age = 29
emp.HomeAddress.PostalCode = "150-0001"
emp.HomeAddress.Prefecture = "東京都"
emp.HomeAddress.City = "渋谷区"
MsgBox emp.Name & "さんの住所:" & vbCrLf & _
emp.HomeAddress.PostalCode & " " & _
emp.HomeAddress.Prefecture & emp.HomeAddress.City
End Sub
emp.HomeAddress.Prefectureのように、ドットを重ねて入れ子になったメンバーにアクセスできます。関連する情報をグループ化して整理したい場合に便利な書き方です。
プロシージャの引数としてユーザー定義型を渡す
ユーザー定義型は、Subプロシージャの引数としてそのまま渡すこともできます。関連するデータをまとめて渡せるので、引数の数を減らしてシンプルなコードにできます。
Sub ShowEmployeeInfo(ByRef emp As Employee)
Debug.Print emp.Name & "さんは" & emp.Age & "歳です"
End Sub
Sub CallShowEmployeeInfo()
Dim emp As Employee
emp.Name = "高橋美咲"
emp.Age = 24
Call ShowEmployeeInfo(emp)
End Sub
引数として渡す際は、既定でByRef(参照渡し)になります。値渡し(ByVal)にしたい場合は明示的にByValを指定してください。
Enum(列挙型)との違い
似たような機能に「Enum(列挙型)」がありますが、役割が異なります。
- Enum:あらかじめ決まった選択肢(定数)に名前を付けるための機能。例:曜日、ステータス区分など
- Type:種類の異なる複数のデータをひとつにまとめるための機能。例:氏名+年齢+入社日のようなレコード情報
「決まった選択肢から選ぶ」ならEnum、「複数の異なる情報をワンセットで扱う」ならTypeと使い分けるとよいでしょう。
使用時の注意点
Type…End Typeを使う際は、以下の点に注意してください。
- 宣言場所:標準モジュールの宣言セクション(先頭、プロシージャの外側)に記述する必要があります。プロシージャの中には書けません。
- Publicにする場合:クラスモジュールやユーザーフォームから参照したい場合は、標準モジュールで
Public Type 型名のようにPublicを付けて宣言します。 - オブジェクト型は不可:メンバーにRangeやWorksheetなどのオブジェクト型を含めることはできません。オブジェクトを扱いたい場合はクラスモジュールの利用を検討してください。
まとめ
Type…End Typeを使うと、種類の異なる複数のデータをひとつのまとまりとして扱えるようになり、特に配列やシートからの一括読み込みと組み合わせたときにコードの見通しが大きく改善します。
- 標準モジュールの宣言セクションで
Type ~ End Typeを定義する 変数名.メンバー名でアクセスする- 配列やプロシージャの引数と組み合わせることで実務データの管理がしやすくなる
- 選択肢を扱うEnumとは役割が異なるので使い分ける
複数の関連データをまとめて扱いたい場面に出会ったら、ぜひType…End Typeを活用してみてください。


コメント