🚀The world's best VBA AI has evolved. ExcelMaster is now an autonomous Agent.Read more →
Back to Blog

Excel VBA のクラスモジュール — 自分だけのオブジェクトを作る(設計図 vs インスタンス、そして As New の罠)

|

Excel VBA のクラスモジュール — 自分だけのオブジェクトを作る(設計図 vs インスタンス、そして As New の罠)

TL;DRクラスモジュールは、自分だけのオブジェクト型のための設計図です。 一度書けば、あとは New で好きなだけ独立したインスタンスを作れます。誰もが つまずくことが 2 つあります。クラスモジュールの名前が型名であること (中に Class キーワードは存在しません)、そしてオブジェクトは参照型なので、 Set b = aab同じインスタンスの 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 はオブジェクトをコピーしません参照をコピーするので、ab は今や同じインスタンスを指します。

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 を備えたクラスを書いてくれます。NewSet があなたを驚かせるあらゆる 手口を覚えておかなくても、VBA らしく罠のないオブジェクト指向 VBA が手に入ります。

よくある質問

VBA のクラスモジュールとは何ですか?

クラスモジュールは、自分だけのオブジェクト型を定義する場所です — データ(フィールド)と 振る舞い(メソッドとプロパティ)をまとめた設計図です。いったん 定義すれば、New でその独立したインスタンスを作れます。これは、データのみを保持する Type の一歩先で、VBA がカスタムオブジェクトをサポートする 仕組みです。

VBA でクラスのインスタンスを作成するには?

クラス型の変数を宣言し、SetNew で新しいインスタンスを代入します。 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