ゆるおたノート

Tomorrow is another day.

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

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

【VBA】クラスの作り方を整理してみた(移行手順編) - ゆるおたノート

クラス

意味

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

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

インスタンス

実際にクラスを利用する時は、下記どちらかの段階でNewキーワード*1を使って「クラス(=設計図)からオブジェクト(=実体)を生成」する。

この生成されたオブジェクトのことをインスタンスと呼ぶ。

モジュール

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

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

スコープ

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

過去記事に、掲載当時に私が理解していた範囲でまとめています。

プロパティ

インスタンスの内容や状態に関する情報*2。多くは「属性」と訳される。
VBAでは、ブックの名前やセルの値、場所、オブジェクトの参照値、など。

情報をインスタンス自身に持たせて、「●●の値を取得する」という時や「値を設定する」といった時に使う。
スコープをPublicにすることで、外のモジュールからインスタンス.プロパティ名で呼び出せるようになる。

プロシージャで定義する場合、使用するプロシージャは機能別に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キーワード*3が入っている。

使用例
'[クラスモジュール] 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.

プロパティ プロシージャ (VBA) を作成する | Microsoft Docs

同じ名前の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では、引数の初期値には定数(もしくは定数式)しか指定できない
ということは、Optionalな引数にプロパティの値を指定したい時は一工夫が必要そう…(調べ中)

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

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

あとがき

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

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

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

このシリーズについて

初心者の頭でVBAでクラスを作成する方法の整理に挑戦しています。

連載目次

  1. オブジェクト指向って、なんだ? - ゆるおたノート
  2. 【VBA】クラスの作り方を整理してみた(移行手順編) - ゆるおたノート
  3. 当記事【VBA】クラスの作り方を整理してみた(基本用語編) - ゆるおたノート

注釈

*1:<2019/10/14追記>
当初「Newステートメント」と書いていましたが、「Newキーワード」の誤りでした。誤解を招く表現、大変失礼いたしました…

▼参考

そして、ご指摘頂いたチン☆テクラ(id:akashi_keirin)さん・鵜原パソコンソフト研究所さん、ありがとうございます!

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

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