Edited at

VBAでクラスをつくる

VBAでのクラスの作り方です。

2019/10/12: 一部文言等、修正しました。


クラスモジュールを挿入する

image.png

こんな感じでクラスモジュールを挿入します。


クラスの名前を付ける

image.png

プロパティウィンドウの「(オブジェクト名)」の欄でクラスの名称を設定します。

これで空のクラスができました。


メンバ変数を定義する

クラスのメンバ変数を定義してみます。


Person(クラスモジュール)

Option Explicit

' メンバ変数
Public Name As String
Private Age As Long


Privateとつけたものはクラスの外からはアクセスできません。

Publicは外からアクセスできます。

(アクセス修飾子といいます)


メソッドを作る

メソッドを追加した例です。


Person(クラスモジュール)

Option Explicit

' メンバ変数
Public Name As String
Private Age As Long
Public Mother As Person

' メソッド
Public Sub SayHello()
MsgBox "Hello, I'm " & Name & "!"
End Sub

' 引数付き
Public Sub SayHelloTo(otherPersonName As String)
MsgBox "Hello, " & otherPersonName & "!" & vbNewLine & _
"I'm " & Name & "!"
End Sub

' 戻り値あり(関数)
Public Function GetAge() As Long
GetAge = Age
End Function

' オブジェクトを返す関数(Setをつけること!)
Public Function GetMother() As Person
Set GetMother = Mother
End Function


メンバ変数と同様、頭にPublicをつけると外からアクセスでき、Privateではアクセスできません。

オブジェクト型を返す時は Set をつける必要があります。


Meキーワード

Meはクラスモジュールでは自身のインスタンスを指します。

image.png


InitializeとTerminate

Class_Initializeはクラスのインスタンスが生成されたときに呼ばれます。引数はつけられません。

Class_Terminateはクラスのインスタンスが破棄されたときに呼ばれます。

ただし、ガーベージコレクションが動くタイミングは必ずしも分からないので、リソース(ex. DBへの接続など)を解放するメソッドを別に作るのが良いといわれています。


Person(クラスモジュール)追加分

' 初期化処理

Private Sub Class_Initialize()
Name = "Bob"
Age = 30
Set Mother = Nothing

Debug.Print "initalized"
End Sub

' 終了時処理
Private Sub Class_Terminate()
Name = ""
Age = 0
Set Mother = Nothing

' その他、使用しているリソースの解放処理を行う
'

Debug.Print "terminated"
End Sub


これらのメソッドはPrivateにします。Publicにするとintellisense(コード補完)で見えてしまいます。


クラスの使い方

クラスの使い方です。


標準モジュール

Option Explicit

Sub test1()
' オブジェクト型の変数を宣言
Dim p As Person

' インスタンス生成
Set p = New Person

' 値をセットする
p.Name = "Bob"

' 値を取得してみる
Debug.Print "age: " & p.GetAge()

' メソッド呼び出し
Call p.SayHello

' オブジェクト破棄(スコープから出るとガーベージコレクションによって勝手に破棄されます)
Set p = Nothing
End Sub



結果

image.png

image.png


プロパティ プロシージャを使う

先ほどの例ではメンバ変数Nameに外部から直接値を設定していましたが、それだと例えば空文字列ならエラーを出すといった処理ができません。

メンバ変数の設定時や取得時に何か処理をするには、プロパティ プロシージャを使います。


Person(クラスモジュール)

Option Explicit

' メンバ変数
' NameとMotherをPrivateにした
Private Name As String
Private Age As Long
Private Mother As Person

' 初期化処理
Private Sub Class_Initialize()
Name = "Bob"
Age = 30
Set Mother = Nothing

Debug.Print "initalized"
End Sub

' 終了時処理
Private Sub Class_Terminate()
Name = ""
Age = 0
Set Mother = Nothing

' その他、使用しているリソースの解放処理を行う
'

Debug.Print "terminated"
End Sub

' メソッド
Public Sub SayHello()
MsgBox "Hello, I'm " & Name & "!"
End Sub

' 引数付き
Public Function SayHelloTo(otherPersonName As String) As Person
MsgBox "Hello, " & otherPersonName & "!" & vbNewLine & _
"I'm " & Name & "!"
End Function

' プロパティプロシージャ
Property Get MyName() As String
MyName = Name
End Property

Property Let MyName(namae As String)
If namae = "" Then
' 氏名がブランクならエラー
Err.Raise 10000, , "名前がブランクです"
End If

Name = namae
End Property

Property Get MyAge() As Long
' 5歳さばを読んでみる
MyAge = Age - 5
End Property

Property Get MyMother() As Person
Set MyMother = Mother
End Property

Property Set MyMother(haha As Person)
Set Mother = haha
End Property


値を設定するときは、値型ならばLet、オブジェクト型ならばSetを使います。値の取得は、どちらもGetです。

なお、ここでMyAgeGetのプロパティ プロシージャしかないので、readonlyプロパティとか言ったりすることもあります。

使い方です。


標準モジュール

Option Explicit

Sub test1()
Dim p As Person

' インスタンス生成
Set p = New Person

' 値をセットする
p.MyName = "Bob"

' オブジェクト型をセットする
Dim mom As Person
Set mom = New Person
Set p.MyMother = mom

' 値を取得してみる
Debug.Print "age: " & p.MyAge

' メソッド呼び出し
Call p.SayHello

' オブジェクト破棄(スコープから出るとガーベージコレクションによって勝手に破棄されます)
Set p = Nothing
End Sub



結果

image.png

image.png


Nothing

オブジェクト型で変数を宣言した後、初期状態だとNothingが変数に入っています。(JavaとかでのNullに相当)

Nothingかどうか検査するには、If p Is Nothing Then ...のようにIsを使います。


関連

クラスの応用編はこちら→https://qiita.com/Kamo123/items/a39c8f3ffdc5e665de98


参考文献

VBA Developer's Handbook: Edition 2

https://www.google.co.jp/search?q=VBA+Developer's+Handbook%3A+Edition+2&ie=&oe=

VBAについて網羅的に載っている本です。

現在は絶版になっているようですが、google playストアで電子版を買えました。