はじめに
前回の「【VBAクラス入門】第2回:クラスモジュールの追加方法とプロパティとメソッドの基本構成」ではクラスがどのようなものか冷蔵庫を例にしてコードを紹介しましたが、そのクラスがどのような構成なのかについてはあまり触れませんでした。
今回はクラスの中身を丁寧に紹介します。
キーワードになるのは、内部データ、クラスの状態、プロパティ、メソッド、Initialize、Terminate です。
クラスの構成要素とは?
クラスで扱う構文には以下があります。
要素 | 説明 | VBAでの構文キーワード | 役割の例 |
---|---|---|---|
1. 内部データ構造 | データを保持する本体 | Dim m_Items As Collection など | 登録データを格納する |
2. クラスの状態 | クラスの状態を保持する | Private m_PowerOn As Boolean など | クラスの状態のOn/Offを切り替える |
3. プロパティ | データの出し入れ口 | Property Get / Let / Set | 名前、IDなどの基本情報 |
4. メソッド | 操作・動作を定義 | Sub / Function | データの追加、表示、削除など |
5. 初期化処理 | 初期状態の設定 | Class_Initialize | Collection や Dictionary の準備 |
6. 終了処理 | 終了時の後処理 | Class_Terminate | ログ出力、後片付けなど |
1. 内部データ構造(一番大事!)
クラスは「データ」と「処理(メソッド)」をひとまとめにした構造です。その中でも特に重要なのが「内部データ」です。なぜなら、クラスは本質的に「内部データを適切に扱うための仕組み」だからです。
多くの入門サイトや書籍では、構文やプロパティ・メソッドの書き方に焦点を当てていますが、それだけでは「クラスをなぜ使うのか」が分かりません。クラスは単に機能の集合ではなく、「あるデータをどう扱うか」を整理・管理するための単位です。
たとえば、クラス内で扱うデータが複数存在し、追加・削除といった操作が発生する場合は、Collection
や Dictionary
を用います。また、クラスの状態を管理する目的で、Boolean
や Integer
などの基本型を使うこともあります。
内部データは、外部から直接アクセスできないように Private
修飾子を付けて定義します。一般的には、クラスモジュールの先頭付近に記述します。これにより、外部からのアクセスは専用のメソッドを介して行う設計になります。
構文:
Private 内部データ名A As Collection
Private 内部データ名B As Dictionary ‘// Dictionaryを使うには「Microsoft Scripting Runtime」への参照設定が必要
Private 内部データ名C As Object
Dictionaryを使う場合:
Dictionaryを使う場合は事前にVBA画面のツールメニュー→参照設定を選び、参照設定ダイアログで「Microsoft Scripting Runtime」にチェックを付けます。
Dictionaryの詳細については「VBAのDictionaryの使い方(全メソッドとプロパティ網羅)」をご参照ください。
コード例:
1 2 3 4 5 6 7 8 9 |
Private m_Items As Collection '// 冷蔵庫に入っている物一覧 Private m_NoteMap As Object '// Dictionary を使って補足情報も持たせられる '// クラス初期化 Private Sub Class_Initialize() '// 領域確保(これ以降、データを追加できるようになる) Set m_Items = New Collection Set m_NoteMap = CreateObject("Scripting.Dictionary") End Sub |
2. クラスの状態
クラスの「内部データ」とは別に、クラス自身の状態(ステータス)を保持するメンバ変数も定義します。
たとえば、「電源がONかOFFか」「製氷機が有効かどうか」「製造番号」など、クラスの動作や存在に関わる情報は「状態」とみなされます。
このような状態は、原則として Private
にして外部から直接アクセスできないようにするのが推奨される設計です。必要に応じて、プロパティ
を通じて安全に読み書きできるようにします。これは、クラスの使用者が予期しない形で状態を変更することを防ぎ、安定した動作を保証するためです。
ただし、クラスを個人用・一時的に使う場合など、厳密なカプセル化が不要なときは Public
で定義しても構いません。ただし、その場合も後々の保守性や拡張性に注意する必要があります。
構文例:
1 2 3 |
Private m_PowerOn As Boolean ' 冷蔵庫の電源ON/OFF状態 Private m_SerialNumber As String ' 冷蔵庫の製造番号 Private m_IceMakerEnabled As Boolean ' 製氷機の稼働ON/OFF状態 |
状態の値にアクセスするには、後述するプロパティを用意して外部に公開する設計が理想です。
例(読み取り専用プロパティ):
1 2 3 |
Public Property Get PowerOn() As Boolean PowerOn = m_PowerOn End Property |
このようにすることで、内部状態のカプセル化を保ちつつ、必要な情報だけを安全に公開することができます。
3. プロパティ
プロパティは、クラスが持つ値の参照や書き換えを行う際に使います。
第2回での冷蔵庫クラスであれば、冷蔵庫の電源ON/OFF状態や、冷蔵庫の製造番号を参照したり、製氷ON/OFFを設定したりする際にプロパティを使います。このようにクラス自体の状態を参照・編集する場合にプロパティを使います。
一方で、クラス内部のデータ構造の操作や処理(たとえばリストへの追加や削除など)は、プロパティではなくメソッドで行います。
プロパティは通常では外部に公開するためPublicを付けて以下のように関数形式で定義します。Get、Let、Setの3種類があります。代入用のLetとSetでは書き換える値を引数で渡します。
構文:
値の参照(取得)用
Public Property Get プロパティ名A() As String
値型データの書き換え(代入)用
Public Property Let プロパティ名B(引数 As String)
オブジェクト型のデータの書き換え(代入)用
Public Property Set プロパティ名C(引数 As Object)
コード例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
'// 電源状態(読み取り専用) Public Property Get PowerOn() As Boolean PowerOn = m_PowerOn End Property '// 製造番号(読み取り専用) Public Property Get SerialNumber() As String SerialNumber = m_SerialNumber End Property '// 製氷機のON/OFF状態取得(読み取り専用) Public Property Get IceMakerStatus() As Boolean IceMakerStatus = m_IceMakerEnabled End Property '// 製氷機のON/OFF設定(書き換え用) Public Property Let IceMakerEnabled(OnOff As Boolean) m_IceMakerEnabled = OnOff End Property |
4. メソッド
メソッドは、外部に公開する場合はPublicを付け、公開しない場合はPrivateを付けます。メソッドの構文は、標準モジュールで定義する Sub や Function と同じ形式で記述します。
Publicを付けるメソッドの用途は、外部から何らかの操作をクラスが実行する際に利用します。特に、内部データを操作したい場合に利用します。例えば、内部データの追加や削除を行う場合や、内部データの個数を取得する場合などが該当します。内部データに関わらない場合でPublicを付ける場合もありますが、いずれにしても外部からクラスに対して何かを行いたい、またはクラスに何かをしてほしい場合になります。
Privateを付けるメソッドの用途は、クラス内で完結する処理を行う場合に利用します。エラー処理やログ出力処理などが該当します。
メソッドには必要に応じて引数を渡すことが可能です。
構文:
Public Sub 公開メソッド名(引数)
Private Sub 非公開メソッド名(引数)
例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
'// データ追加メソッド Public Sub AddItem(itemName As String, Optional note As String = "") m_Items.Add itemName If note <> "" Then m_NoteMap(itemName) = note End If End Sub '// 全データ参照メソッド Public Sub ShowItems() Dim i As Variant For Each i In m_Items Debug.Print i & ":" & IIf(m_NoteMap.exists(i), m_NoteMap(i), "(メモなし)") Next End Sub |
5. 初期化処理
Class_Initialize()は外部処理からクラスをNewしたときに自動で呼ばれます。「Class_Initialize」という名前はクラスモジュール内で固定です。クラス開始時の初期化処理が不要であればClass_Initialize()は書かなくても構いません。
クラスの状態の初期化など、初期状態の設定はここで行います。例:CollectionをNewする。
Class_Initialize()は他のプログラミング言語でのコンストラクタとほとんど同じです。
構文:
Private Sub Class_Initialize()
‘// ここに初期化処理を書く
End Sub
例:
1 2 3 4 |
Private Sub Class_Initialize() Set m_Items = New Collection Set m_NoteMap = CreateObject("Scripting.Dictionary") End Sub |
6. 終了処理
Class_Terminate()はクラスの利用が終了する際に自動で呼ばれます。また、Set ~ = Nothing にしたタイミングでも呼ばれます。クラス終了時の処理が不要であればClass_Terminate()は書かなくても構いません。
Class_Terminate()は他のプログラミング言語ではデストラクタなどと呼ばれるものになります。
用途として多いのは、終了メッセージの表示や、開いているファイル(ログファイル等)を閉じたりするような場合です。
構文:
Private Sub Class_Terminate()
‘// ここに終了処理を書く
End Sub
例:
1 2 3 4 |
Private Sub Class_Terminate() Debug.Print m_Name & " を片付けています..." '// 特別な後処理があればここに書く End Sub |
まとめ
クラスとは、「内部データを安全に管理・操作する仕組み」であり、単なる構文の塊ではありません。
クラスの構成要素は、主に「内部データ構造」「状態」「プロパティ」「メソッド」「初期化」「終了処理」の6つです。
特に重要なのは「内部データ」の設計であり、CollectionやDictionaryを適切に使うことで、クラスの実用性が大きく向上します。
状態を管理するメンバ変数にはプロパティを通じてアクセスすることで、カプセル化と保守性を保つことができます。
メソッドは、クラスに何か「してもらう」ための仕組みであり、外部とのインターフェースとなる役割を果たします。
クラスの設計は一見難しそうに見えますが、「どんなデータを扱い、どう安全に操作したいか」を軸にすれば自然と必要な構成が見えてきます。