プロパティーに値を代入するという表現をよく見かける。
私もつい最近までそのように表現していたけれど、これって厳密には間違ってるんじゃないかと思い始めている。
というのも、Propertyプロシージャを学習する際、この「代入」という表現が理解を難しくさせている気がするのだ。
プロパティに代入してるんじゃなくて、プロパティが代入している。
プロパティに値を代入するなんて言うけど、実際にProperty Letプロシージャ自体に値を代入している訳ではない。
どういうことなのか、コードを使って説明しよう。
たとえば以下のようなCounterクラスを作成する。
Private num As Long Public Property Get Number() As Long Number = num End Property Public Property Let Number(n As Long) num = n End Property Public Sub Plus(Optional n As Long = 1) num = num + n End Sub
標準モジュールには次のコードを張り付ける。
Sub hoge() Dim C As Counter Set C = New Counter C.Number = 10 Debug.Print C.Number End Sub
ここで、C.Number = 10という文は、あたかもプロパティに値を代入しているように見えるだろう。単にクラスを使う方は、そのように理解してもまあ差し支えない。
でもいざ自分でPropertyプロシージャを使おうと思ったら、「代入」という表現は理解を難しくさせる。
C.Number = 10という文が行っているのは、NumberのLetプロパティーへ引数を渡す行為である。NumberのLetプロパティは仮引数nで渡された10を受け取り、それをクラスのプライベート変数であるnumに代入している。
つまり代入という行為はあくまでPropertyプロシージャの手続きの中で書かれたコードによって行われるのであって、C.Number = 10という書き方が、Numberプロパティへの代入を意味する訳ではないのだ。
もっといえば、Numberプロパティが受け取った仮引数をどうするかはクラス設計者にゆだねられており、代入せずに破棄してしまうこともできる。
先ほどのCounterクラスでNumberのLetプロパティを書き換えてみた。
Private num As Long Public Property Get Number() As Long Number = num End Property Public Property Let Number(n As Long) '何もしない End Property Public Sub Plus(Optional n As Long = 1) num = num + n End Sub
これでもメインコードは動作するが、イミディエイトウインドウでは0が表示される。
Propertyプロシージャはオブジェクトのプロパティを作るために作られた特別なプロシージャであるが、「プロシージャ」というからには「手続き」なので、SubやFunctionに書くようなふつうの命令も記入できる。
こんな風に。
Public Property Let Number(n As Long) MsgBox "代入する、と見せかけて" MsgBox "受け取った「" & n & "」は破棄します。" End Property
また、クラスモジュールで用いるイメージが強いPropertyプロシージャだが、クラスでなくても書くことができる。
以下は標準モジュールでPropertyを活用した例。
thom.hateblo.jp
さらにVBA自体でも、標準モジュールのPropertyが活用されている。
thom.hateblo.jp
まあ、あくまでプロパティを作るものとしてPropertyが用意されているので、あまり逸脱した使い方はお勧めできないが、ここで言いたかったのはPropertyだって、SubやFunctionと同じように「手続き」なんだということ。
Javaの用語で学ぶクラスモジュール
VBAにおけるPropertyが一体何者なのか明らかにするために、Java用語を借りてこようと思う。
今から説明する用語はJavaに限った話ではなくてオブジェクト指向の一般的な用語だとは思うけれど、それ以外の言語に疎いのと、現にVBAでは聞かない用語もあるので。
まずクラスというものは、フィールドとメソッドから成る。
フィールドというのは、クラスが配下に持つ変数だ。メソッドというのはいわゆるプロシージャである。
そしてフィールドとメソッドを包括して、メンバと呼ぶ。
さて、VBAでクラスモジュール「counter」を作ってみる。
このとき、Numberはクラスのパブリックフィールド、Plusはパブリックメソッドである。
Public Number As Long Public Sub Plus(Optional n As Long = 1) Number = Number + n End Sub
ちなみにJavaにはProperty構文がない。
だから基本的には普通のメソッドを使ってデータアクセスを制限する。
VBAではJavaと同じようにPropertyを使わずにデータアクセスを制限することもできる。
その場合は、このような書き方になる。
Private num As Long Public Function GetNumber() As Long GetNumber = num End Function Public Sub LetNumber(n As Long) num = n End Sub Public Sub Plus(Optional n As Long = 1) num = num + n End Sub
ここでnumはプライベートフィールド、GetNumberとLetNumberはメソッドである。
プライベートフィールドに間接的にアクセスするためのメソッドのことを「アクセサ」と呼ぶ。
メインコードでは、値の設定と取得で別のアクセサメソッドを呼び出す必要がある。
Sub hoge() Dim C As Counter Set C = New Counter C.LetNumber 10 Debug.Print C.GetNumber End Sub
ではVBAのPropertyは何かというと、このアクセサを作成するための専用構文なのである。
これを使うと遥かに便利になる。もう一度冒頭のコードを見てみよう。
■クラスモジュール「Counter」のコード
Private num As Long Public Property Get Number() As Long Number = num End Property Public Property Let Number(n As Long) num = n End Property Public Sub Plus(Optional n As Long = 1) num = num + n End Sub
■標準モジュールのコード
Sub hoge() Dim C As Counter Set C = New Counter C.Number = 10 Debug.Print C.Number End Sub
Javaではメソッドを通じてプライベートフィールドにアクセスするのだが、VBAのProperty構文ではあたかもパブリックフィールドに直接アクセスしているかのような感覚でアクセサを使用することができる。
内部的な動作はメソッドであるにもかかわらず、外からみたらパブリックフィールドのように扱えるもの。これがPropertyプロシージャである。
VBAの用語で言い直すと、動作はプロシージャ、外面は変数ってところか。
繰り返しになるが、C.Number = 10という文が行っているのは、NumberのLetプロパティーへ引数を渡す行為である。
カッコの代わりに、イコールで引数を渡せるようになっただけで、引数であることに変わりはない。
まとめ
Property…
フィールドのフリをしているが、そいつはメソッドだ!
気をつけろっ!
まぁJavaみたいに「フィールド・メソッド」という分類ではなく、ExcelVBAで広く説明されている「プロパティ・メソッド」という分類に従うと、Propertyは明らかにプロパティなんだけどね。
ということで、今までどおりプロパティに代入という表現を使って良いと思うけれど、内部動作としてはプロシージャで、代入のフリをした引数渡しであることをしっかり押さえておくと混乱なく使いこなせるかなと思った次第。
最後に注意点として、Propertyはフィールドのように使われることが想定されるので、あまり重たい処理をプロパティの中で行うのは好ましくない。せいぜい書き込み時のチェックくらいにしておいたほうが良い。また今回メッセージボックスを表示させるサンプルも書いたけれど、あれはSubやFunctionと同じプロシージャであることを説明するためなので、普通はそんな使い方はしない。Propertyはあくまで「アクセサ」を実現するためのプロシージャなのだ。