どうも。つじけ(tsujikenzo)です。こちらのシリーズでは2021年1月より受講しました「ノンプロ研VBA中級講座2期」で学んだことをアウトプットしております。本日はDay2です。
前回の講座
前回は「スコープとプロパティ」についてでした。スコープは早見表を作って参照すればいいのですが、「なぜ変数の適用範囲が効くのか」という考察を別記事でしてしまったため大変な目にあいました非常に勉強になりました。
プロパティについてもパブリック変数で作成するほか、Property Let/Set Getプロシージャによる操作をはじめて習いました。そういえばGAS中級で習った「ゲッターセッター」とおなじだったので、なんとかくらいつくことができています。
今日は「モジュール」についてです。アジェンダはこのようになっております。
- モジュール
- プロパティ
- メソッド
モジュール
VBAには大きく分けて「標準モジュール」と「オブジェクトモジュール」の2種類があります。
標準モジュール
「標準モジュール」と「オブジェクトモジュール」の違いをまとめると、下記表のようになります。これは、標準モジュールで記述したパブリックメンバーは、すべてグローバルのメンバーになるということを表しています。プロジェクト全体で管理する(してしまう。)ので、どこからでも呼び出せる半面、管理コストが高くなります。
オブジェクトモジュール
一方、オブジェクトモジュールのモジュールレベルに宣言されたパブリックメンバーは、そのオブジェクトに属するため(オブジェクト内に閉じ込めるとも言えますね)、管理するのが楽になります。もちろん、オブジェクトブラウザーのグローバルにも表示されません。
メンバーの呼び出し
このことから、主にオブジェクトモジュールに記載すべき(一度に面倒みるべき)メンバーは下記のようになります。
たとえば、各シートでしか使わないプロパティなどは、オブジェクトモジュール用のメンバーとすれば、「オブジェクト.メンバー」と書かないとアクセスできないため、標準モジュールもスッキリすることができます。
【MEMO】「Me.」オブジェクトモジュールで使える自分自身を表すオブジェクト。
プロパティ
Day1のおさらいです。VBAではオブジェクトにプロパティを追加する方法は3つあります。
- モジュールレベル変数(Public|Private)
- Property Let/Setプロシージャ
- Property Getプロシージャ
Day1では標準モジュールにパブリック変数を書くことで、プロパティを作成することを学びました。「VBAで一番かんたんなプロパティの作り方」でしたね。
Public 変数名 As データ型
これを「オブジェクトモジュールでやってみよう」というのがDay2の演習です。
そして、パブリック変数じゃなくて、「プライベート変数」としてプロパティ定義するとどうなるかという演習を行います。しかし、(当然ながら)プライベート変数は他のモジュールから呼び出せないので、これは意味がないのでは?と思われます。
これを「ゲッター・セッター」という仕組みでうまく扱ってみましょう。
Property Let/Setプロシージャによるプロパティの定義
前回のおさらいです。プロパティを定義する方法として、標準モジュールに記載するProperty Let/Setプロシージャがありました。
[Public] Property {Let|Set} プロシージャ名(引数リスト, 値) 処理 End Property
これを、オブジェクトモジュールに記述することで、オブジェクトモジュール用のセッターを作成することができます。
セッターとは
プロパティに値を設定するプロシージャです。プロシージャなわけですから「マイナスの値は受け付けない」とか「すでに値があるなら上書き禁止にしたい」などの処理が可能です。
[定義側] Property Let/Set 変数名 (引数名 As データ型) Private変数_ = 引数(値) End Property [呼び出し側] オブジェクト.変数名 = 値 [結果] 値をPrivate変数_(プロパティ)に設定する。
Property Getプロシージャによるプロパティの取得
こちらも前回のおさらいです。前回は標準モジュールに記載しました。
[Public] Property Get プロシージャ名(引数リスト) As 型 処理 プロシージャ名 = 値 End Property
これを、オブジェクトモジュールに記述することで、オブジェクトモジュール用のゲッターを作成することができます。
ゲッターとは
プロパティから値を取得するプロシージャです。プロシージャなわけですから「処理を加えてから取り出す」とか「読み取り専用プロパティをつくりたい」などの処理が可能です。
[定義側] Property Get 変数名 ( ) As データ型 変数名 = Private変数_ End Property [呼び出し側] オブジェクト.変数名 [結果] Private変数_(プロパティ)を取得する。
読み取り専用プロパティ
ゲッターとセッターをもちいることで、2つのタイプのプロパティを使い分けることができます。
- 読み書き可能なプロパティ
- パブリック変数
- 同名のつがいになったProperty Let/SetプロシージャとProperty Getプロシージャ
- 読み取り専用プロパティ
- Property Getプロシージャのみ
【MEMO】プロパティの一時的な値の置き場として使われるプライベート変数は、変数名の後ろ(尻尾のイメージ)にアンダースコア_を付けるとわかりやすいです。【MEMO】STOPしたときにイミディエイトウィンドウで「? 変数名」で、格納されてる値を確認することができます。変数名にマウスをホバーでも値を確認することができます。
【MEMO】Property Let/Set プロシージャがなかったら、オブジェクトブラウザーで「読み取り専用」と表示されます。
この章のまとめ
プロパティの定義を省略していくと「変数への代入になる」と書きましたが、Property Let/Set/Getプロシージャによって値の取り出し時に処理ができるのが、変数との違いです。
Property Get 変数名( ) As データ 変数名 = Private変数_ End Property Property Let/Set 変数名(引数名 As データ型) Private変数_ = 引数名(値) End Property
の組み合わせで、プロパティにアクセスするというお話でした。これは次回「クラス」にもつながる話です。
メソッド
オブジェクトモジュールにメソッドを追加する方法は2種類あります。
- Subプロシージャ
- Functionプロシージャ
[Public] Sub プロシージャ名(引数リスト) 処理 End Sub
[Public] Function プロシージャ名(引数リスト) As 型 処理 プロシージャ名 = 戻り値 End Function
オブジェクトに対するメンバー(プロパティ、メソッド)の実装方法と処理(戻り値)についてまとめるとこのようになります。
VBA、どのプロシージャを使うか問題
ひねくれものの私としては「戻り値を返さないFunctionプロシージャを書けば、Subプロシージャは要らないじゃないか」と思ったりします。
これはPropertyプロシージャも同様で、Functionプロシージャに引数を渡して、プライベート変数にアクセスさせたり戻り値を返すことも可能なため、「ゲッター・セッターは不要じゃないか?」という発想にもなりかねません。
しかし、VBAにプロシージャが5つもあるのは、なにかしらの使い分けを想定して設計されていますので、もともと定義されているクラスなどの整合性に合わせていくほうがいいとのことです。下記を意識することで、リーダブルコードにもつながります。
- メソッドを定義したい場合・・・Sub/Functionプロシージャ
- プロパティを定義したい場合・・Propertyプロシージャ/パブリック変数
識別子と記法について
補講で「ライブラリ」について質問があったようですね。VBAではライブラリを自作して組み込むより、クラス定義を複製することが多いかなというお話でした。ただし、バージョン一括管理ができないため、どこで使用したかメモしておく必要があるようです。
まとめ
さて、以上で「モジュール」ということで、特に「オブジェクトモジュール」についてお届けしました。
くどいようですが、オブジェクトモジュールのプロパティは、他のモジュールから操作できません。そこでゲッター・セッターをもちいると、「外部から直接書き換えることができないけど、セッター経由なら値を定義したり、ゲッター経由なら呼び出しの際に処理を加えることができる」というなんとも便利なプロシージャのご紹介でした。
次回はいよいよ「クラス」です。振り落とされないように必死にくらいついて生きたいと思います。