TL;DR — クラスモジュールは、自分だけのオブジェクト型のための設計図です。 一度書けば、あとは
Newで好きなだけ独立したインスタンスを作れます。誰もが つまずくことが 2 つあります。クラスモジュールの名前が型名であること (中にClassキーワードは存在しません)、そしてオブジェクトは参照型なので、Set b = aがaとbを同じインスタンスの 2 つの名前にすることです。Dim x As New clsThingよりも、明示的なSet x = New clsThingを選びましょう。 前者は遅延インスタンス化の罠を隠しています。
' --- clsEmployee という名前のクラスモジュール内(プロパティ ウィンドウで名前を変更する)---
Private mName As String
Private mSalary As Currency
Public Property Get Name() As String: Name = mName: End Property
Public Property Let Name(v As String): mName = v: End Property
Public Property Get Salary() As Currency: Salary = mSalary: End Property
Public Property Let Salary(v As Currency): mSalary = v: End Property
Public Function AnnualCost() As Currency ' 振る舞いはデータと共に置く
AnnualCost = mSalary * 1.3 ' 給与 + 30% の間接費
End Function
' --- 通常のモジュール内 ---
Sub Demo()
Dim e As clsEmployee
Set e = New clsEmployee ' インスタンスを 1 つ作成する
e.Name = "Sarah"
e.Salary = 65000
MsgBox e.Name & " costs " & e.AnnualCost ' 84500
End Sub
Type はフィールドをまとめます。クラスモジュールはもう一歩
先へ進みます。フィールドと、それに作用するコードをまとめ、作成でき、参照をコピー
でき、Collectionに格納できる本物のオブジェクトを与えて
くれます。これは VBA がオブジェクト指向プログラミングに最も近づく地点であり —
うまく使えば、散らかったバラバラの変数と補助的な Sub の群れを、ひと握りの自己完結
したオブジェクトへと変えます。
この記事で学べること
- 考え方の軸 — インスタンスを打ち出す設計図
- なぜ Class キーワードが存在しないのか(モジュール名が型そのもの)
- インスタンスの作成 — Set ... = New と、As New の罠
- 意外に思われるルール — オブジェクトは共有する、コピーはしない
- 初期化と後始末 — Class_Initialize と、存在しないコンストラクター
- クラスが Type や Collection に勝る場面
考え方の軸:クラスは「設計図」であってインスタンスではない
クラスモジュールは設計図です。New のたびに、そこから 1 つのインスタンスが
作られます。設計図 clsEmployee は、あらゆる従業員オブジェクトが何を持つか
(名前、給与)と何をするか(AnnualCost)を記述します。それ自体は従業員では
ありません — 計画図です。Set e = New clsEmployee は、自分だけのデータを持つ実際の
従業員を 1 つ組み立てます。3 回やれば、それぞれが自分の名前と給与を持つ 3 つの独立した
オブジェクトが手に入り、すべて同じ計画図から作られています。
この分離を手放さないでください。クラスモジュールにまつわる混乱のほとんど —
「なぜクラスの中に名前がないのか?」「なぜ 1 つのオブジェクトを編集したら別の
オブジェクトが変わったのか?」 — は、**設計図(クラス)とインスタンス(New で
作ったオブジェクト)**を切り分けた瞬間に解けます。
なぜ Class キーワードが存在しないのか
他の言語から来た人は、クラスモジュールを開いて Class clsEmployee のような行を
探します。そんな行はありませんし、書けばコンパイルエラーになります。VBA では、
クラスモジュールの名前が型名です。 オブジェクトの型名を変えるには、コード内の
キーワードではなく、プロパティ ウィンドウ(F4)でモジュールの名前を変更します。
つまり手順はこうです。クラスモジュールを挿入し、F4 を押し、(オブジェクト名) を
clsEmployee に設定し(cls という接頭辞が一般的な慣習です)、そのメンバーを直接
宣言し始めます。そのファイルがクラスであり、その名前が、As の後ろに書く型なのです。
インスタンスの作成:Set = New と、As New の罠
オブジェクトを作る方法は 2 つあり、それらは等価ではありません。
' 明示的(推奨)
Dim e As clsEmployee
Set e = New clsEmployee ' まさにここ、この行で作成される
' 自動インスタンス化(すっきり見えるが、2 つの罠を隠す)
Dim e As New clsEmployee ' まだ作成されていない — 最初に使ったときに作成される
As New の形は 1 行で済むので魅力的ですが、本物の問題を 2 つ隠しています。
If e Is Nothingを決してテストできません。As Newでは、確認しようとeに 触れた瞬間に VBA がそれをインスタンス化してしまうので、Is Nothingは実質つねにFalseです。「これは作成されたことがあるのか?」と問う能力を失います。- 自分自身を生き返らせます。
As Newの変数をNothingに設定し、それからまた 参照すると、VBA は無言で新しいインスタンスを作ります — 破棄したつもりだったのに 再び現れるゾンビオブジェクトです。
ルール: Dim e As clsEmployee + Set e = New clsEmployee を選びましょう。予測
可能な行でオブジェクトを作り、Is Nothing を意味のあるものに保つので、未初期化の
オブジェクトをガードできます。そして Set を決して忘れないこと。それを付けずに
e = New clsEmployee と書くと、「オブジェクト変数または With ブロック変数が設定
されていません」(エラー 91) が発生します — オブジェクトは = ではなく Set で
代入する、という値とオブジェクトの同じルールです。
意外に思われるルール:オブジェクトは共有する、コピーはしない
これは Type の記事の鏡像であり、クラスモジュールのバグの最も
深い源です。オブジェクトは参照型です。Set b = a はオブジェクトをコピーしません
— 参照をコピーするので、a と b は今や同じインスタンスを指します。
Dim a As clsEmployee, b As clsEmployee
Set a = New clsEmployee
a.Salary = 50000
Set b = a ' 両方の名前が -> 1 つのインスタンスを指す
b.Salary = 70000 ' 共有されたオブジェクトを書き換える
Debug.Print a.Salary ' 70000 — a が「ひとりでに変わった」
本当に独立した 2 人の従業員が欲しかったのなら、Set b = a はバグです。VBA には
オブジェクトの自動コピーがありません。同じフィールド値を持つ新しいインスタンスを
返す Clone メソッドを自分で書くことになります。そして、そもそも本当に欲しかったのが
代入時コピーだったのなら、それは Type — 値型 — の方が良い
モデルだったという合図です。共有とコピー、どちらのセマンティクスが必要かを知る
ことこそ、両方を理解する全目的です。
初期化と後始末:Class_Initialize と、存在しないコンストラクター
クラスは、インスタンスが生まれるときや死ぬときに、コードを自動で実行できます。
Private Sub Class_Initialize() ' Set e = New clsEmployee で実行される
mSalary = 0 ' ここで既定値を設定する
End Sub
Private Sub Class_Terminate() ' 最後の参照がなくなったときに実行される
' ファイルを閉じる、ハンドルを解放する、など
End Sub
落とし穴はこうです。Class_Initialize は引数を取りません。 VBA にはパラメーター
付きのコンストラクターがありません — New clsEmployee("Sarah", 65000) とは書けない
のです。まずオブジェクトを作り、それからプロパティを設定します。この 2 段階の手順は
面倒で忘れやすいので、標準的な回避策は、通常のモジュールに置く小さなファクトリー
関数です。
Function NewEmployee(nm As String, sal As Currency) As clsEmployee
Dim e As clsEmployee
Set e = New clsEmployee
e.Name = nm
e.Salary = sal
Set NewEmployee = e
End Function
' Set e = NewEmployee("Sarah", 65000) ' 読みやすい 1 行
私の経験則はこうです。 1、2 回より多く作るクラスは、ファクトリー関数に値します。
VBA が省いたパラメーター付きコンストラクターを与えてくれますし、Set とプロパティ
設定の行が散らばる代わりに、作成を 1 か所にまとめてくれます。
クラスが Type や Collection に勝る場面
次のうち少なくとも 1 つが当てはまるなら、クラスモジュールに手を伸ばしましょう。
- データと振る舞いがひとまとまりである — レコードは、自分自身について検証・整形・
計算すべきであって、そのロジックが補助的な
Subに散らばるべきではありません。 - 独立して自分を管理するオブジェクトが多数必要である — 注文、請求書、従業員 —
理想的には
Collectionに保持します。Collectionは オブジェクトを受け入れますが、Typeは受け入れません。 - 内部を隠したい — プロパティを通じてきれいな表面を
公開し、裏にあるフィールドは
Privateに保ちます。
どれも当てはまらず、ただフィールドを安価なコピーで一緒に運びたいだけなら、Type の
方が軽くて正直です。3 つの値の詰め合わせのためにクラスを作ってはいけません — しかし
その詰め合わせが何かをする必要が出た瞬間、クラスがまさに正解です。
ExcelMaster の活用
クラスモジュールは、「コンパイルは通るのに挙動がおかしい」バグが住む場所です。
As New のゾンビ、オブジェクトを「ひとりでに」変えてしまう共有参照の編集、エラー 91 を
投げる忘れられた Set。どれも噛みつくまで目に見えず、どれも、述べるのは簡単なのに
締め切りに追われると忘れやすいルールから来ます。
ExcelMaster なら、
欲しいオブジェクトを説明できます — 「名前、給与、そして年間コストを返すメソッドを持ち、
リストで持っておける Employee」— すると、明示的な Set ... = New、ファクトリー関数、
プロパティを通じて公開される Private の裏フィールド、そしてそれらを保持する
Collection を備えたクラスを書いてくれます。New と Set があなたを驚かせるあらゆる
手口を覚えておかなくても、VBA らしく罠のないオブジェクト指向 VBA が手に入ります。
よくある質問
VBA のクラスモジュールとは何ですか?
クラスモジュールは、自分だけのオブジェクト型を定義する場所です — データ(フィールド)と
振る舞い(メソッドとプロパティ)をまとめた設計図です。いったん
定義すれば、New でその独立したインスタンスを作れます。これは、データのみを保持する
Type の一歩先で、VBA がカスタムオブジェクトをサポートする
仕組みです。
VBA でクラスのインスタンスを作成するには?
クラス型の変数を宣言し、Set と New で新しいインスタンスを代入します。
Dim e As clsEmployee として、次に Set e = New clsEmployee です。Set キーワードは
オブジェクトに必須で、省く(e = New clsEmployee)とエラー 91 が発生します。作成後、
プロパティを設定します。e.Name = "Sarah" のように。
VBA の Dim As New と Set New の違いは何ですか?
Dim e As clsEmployee + Set e = New clsEmployee は、まさにその行でオブジェクトを
作り、If e Is Nothing を意味のあるものに保ちます。Dim e As New clsEmployee は作成を
最初に使うときまで遅らせるため、Is Nothing はつねに False になり、Nothing に
設定してからまた触れると無言でオブジェクトを再作成します。明示的な Set ... = New の
形を選びましょう。
VBA のクラスはパラメーター付きのコンストラクターをサポートしますか?
いいえ。Class_Initialize イベントはインスタンスが作成されるときに自動で実行され
ますが、引数を取らないので、New clsEmployee("Sarah", 65000) とは書けません。標準的な
回避策は、通常のモジュールに置くファクトリー関数です。オブジェクトを作り、プロパティを
設定し、それを返します — オブジェクトを組み立てる、1 行でパラメーター付きの方法を
与えてくれます。
Type や Collection の代わりにクラスモジュールを使うべきなのはどんなときですか?
レコードに振る舞い(メソッド)が必要なとき、Collection に格納できる独立した
オブジェクトが多数欲しいとき、または内部フィールドをプロパティの
裏に隠したいときにクラスを使います。フィールドを安価で独立したコピーとともにただ一緒に
運びたいだけなら、Type の方が軽くて明快です。
検証環境
検証環境: Excel 365 (Windows 11), VBA 7.1 — 最終確認 2026-08-04.
関連ガイド: VBA Type · VBA Property · VBA Dim · VBA Collection · VBA Dictionary
