ゆるおたノート

Tomorrow is another day.

【VBA】クラスの作り方を整理してみた(基本用語編)

先発の記事が長すぎたので、良きところで分割してみました。

www.yuru-wota.com

クラス

今回のメイン。
「Aというオブジェクトは、BプロパティやCメソッドを持っている」といった「定義」を行う。
本やWebページでは、「設計書」や「鋳型」と説明されていることが多い。

VBAでは、「クラスモジュール」を使ってユーザー自身が新しいクラスを作成できる。

インスタンス

実際にクラスを利用する時は、Dimステートメント等の代入(もしくは宣言)の段階で、Newキーワード(※)を使って「クラス(設計図)からオブジェクト(実体)を生成」する。
この生成されたオブジェクトを「インスタンス」と呼ぶ。

※<2019/10/14追記>

当初「Newステートメント」と書いていましたが、「Newキーワード」の誤りでした。

▼Office VBA リファレンス

誤解を招く表現、大変失礼いたしました…
そして、チン☆テクラ(id:akashi_keirin)さん・鵜原パソコンソフト研究所さん、ご指摘いただきありがとうございます!

モジュール

処理の集まり。
VBAでは、標準モジュール、クラスモジュール、シートモジュールがある。

今回は「クラスモジュール」にクラスの設計を記入していく。

スコープ

参照範囲。
「●●の中ならアクセスOK」の『●●』の部分。
プライベートとかパブリックとかグローバルとか、~~レベルと呼ばれる。

いま私が知っている範囲は、過去記事にまとめています。

プロパティ

多くは「属性」と訳される。
インスタンスの内容や状態に関する情報*1インスタンス自身に持たせて、「●●の値を取得する」時や「値を設定する」時に使う。
VBAでは、ブックの名前やセルの値、場所、オブジェクトの参照値、など。

名前は「大文字始まり」の名詞が多い(気がする)。

また、VBAでは機能別にGetLet/Setの3種類のプロシージャに分かれている。

値を取得する

【Property Getプロシージャ】
「プロパティの値を返す」ことのみ出来る。
VBAの仕様上、Property Getプロシージャ内で「プロパティへの代入・設定」は直接できないので、「変数や定数を経由」して値を設定する必要がある。

  • 使用例
'[クラスモジュール(Exampleクラス)]
Private 仮変数 AsProperty Get プロパティ名()As 型
    仮変数 = 123
    プロパティ名 = 仮変数
End Sub
'[標準モジュール]
Sub test()
    Dim ex As New Example
    'インスタンスexのプロパティの中身を確認
    Debug.Print ex.プロパティ名 '「123」と表示される
End Sub

値を代入する

【Property Letプロシージャ】
「プロパティに値を設定する」ためのプロシージャ。
値を代入するときのLet*2

  • 使用例
'[クラスモジュール(Exampleクラス)]
Private 仮変数 AsProperty Let プロパティ名(仮引数 As)
    仮変数 = 仮引数
End Sub
'[標準モジュール]
Sub test()
    Dim ex As New Example
    ex.プロパティ名 = 123 'プロパティに値を代入
End Sub


【Property Setプロシージャ】
「参照するオブジェクトをプロパティに設定する」ためのプロシージャ。
Subプロシージャ等でオブジェクトを代入するときのSetと同じ。

  • 使用例
'[クラスモジュール(Exampleクラス)]
Private 仮変数 As オブジェクト系の型

Property Set プロパティ名(仮引数 As オブジェクト系の型)
    Set 仮変数 = 仮引数
End Sub
'[標準モジュール]
Sub test()
    Dim ex As New Example
     'プロパティにオブジェクトの参照値を代入
    Set ex.プロパティ名 = オブジェクト
End Sub

引数の正しい使い方?

リファレンスによると、同じ名前のプロパティプロシージャは、引数名とデータ型をほぼ同じにしなければならないらしい。

The first argument through the next to last argument (1, …, n) must share the same names and data types in all property procedures with the same name.

(当ブログ訳)同じ名前のPropertyプロシージャ間では、最初の引数~最後から2番目の引数は、同じ引数名とデータ型を共有していなければならない。

ただ、そうなっているクラスをあまり見たことがない気が。
…初心者の私が知っている限りでは。

「実際はどちらでも良い(ことにしてある)」ってことなのかな…?

<2019/10/15追記>
コメントにて、imihito(id:imihito)さんより下記補足いただきました。
ありがとうございます!

リファレンスによると、同じ名前のプロパティプロシージャは、引数名とデータ型をほぼ同じにしなければならないらしい。

に関してはVBAでも常に守られています(条件を満たさないPropertyを作成するとコンパイルエラーになります)。

Property Getの返り値は、Property Set/Letの最後の引数と同じ型にする必要がありますし、
Property Getの引数は、Property Set/Letの最後から1個前の引数までと、名前・型が一致する必要があります(Getに引数が無ければ、Set/Letの引数はGetの返り値と同じ型の1個のみ)

個人的には、最後の1文が重要そうです!

メソッド

命令。
SubプロシージャFunctionプロシージャが使える。
名前は「大文字始まり」で、「●●する」のように動詞にすることが多い(気がする)。

プロシージャをPublicにすることで、外のモジュールからインスタンス.メソッド名で呼び出せるようになる。

'[クラスモジュール(Exampleクラス)]
Sub メソッド名(仮引数 As)
    '処理
End Sub
'[標準モジュール]
Sub 使用例()
    Dim ex As New Example
    ex.メソッド名 引数
End Sub

引数を「省略可」にするには…?

VBAでは、引数の初期値には定数(or定数式)しか指定できない
よって、Optionalな引数にプロパティの値を指定したい時は一工夫が必要そう…(調べ中)

いま頭にあるのは、下記の2つ。

  • プロパティ用の値を変数ではなく定数(モジュールレベル)で定義する
  • プロシージャの中で引数の有無を判定して値を設定する
    → 数値だと初期値が0なので少し面倒かも…

あとがき

ついつい適当な憶測で書いてしまうので、記事をアップする時はこんな弱小ブログでも変な認識・知識を広めちゃったりしないか毎回結構ドキドキです…

でも、今回VBAブロガーの方々に色々ご指摘いただけて嬉しいです。
(全部理解できてるかと言ったら微妙ですが)とっても勉強になります!

…自分でも、もっとよく調べてから書かないとですね。

*1:テレビゲームで言うところの火属性とか、ステータス異常とか、能力値とか。

*2:本来は変数などに代入する時も必要だけど、普段は省略しても動くように作られているらしい。