VB でプログラミングする際、何気なく使っているオブジェクトのプロパティ。
プロパティとは、オブジェクトの状態を表す値(変数)です。
ただし、プロパティは単なる変数ではありません。
プロパティに値を設定することで、オブジェクトの外見が変わったりすることがあります。(たとえば、フォームの Caption プロパティの文字列を変更すると、ウィンドウタイトルが変更されますね。)また、値の取得(参照)だけを許し、書き込みができないものもあります。
ここでは、そんなプロパティの性質や実装方法を見てゆきます。
構造体をクラスに発展させる場合、構造体の各データがオブジェクトのプロパティになります。
'構造体(変数)の内容を操作
With chaHero 'As CharacterType
.Name = "Somebody"
.Level = 1
.HP = 200
.MP = 0
End With
'クラス(オブジェクト)のプロパティを操作
With chaHero 'As CharacterClass
.Name = "Somebody"
.Level = 1
.HP = 200
.MP = 0
End With
クラスを利用する際のコードを見る限り、何も変わりません。
しかし、クラスにプロパティを実装させることは、構造体を定義するように簡単ではありません。
プロパティの立場に立って考えると、プロパティの値は「取得」されるときと「設定」されるときがあります。
Private Sub txtWindowTitle_Change()
Caption = txtWindowTitle.Text
End Sub
フォームに txtWindowTitle という TextBox があるとします。
txtWindowTitle の Text プロパティの値が変更されると、Change イベントが発生し、イベントプロシージャが呼び出されます。
イベントプロシージャでは、フォームの Caption プロパティに、入力された値(文字列)を代入しています。実行すると、フォームのウィンドウタイトル(キャプション)が、テクストボックスに入力した文字列になります。
ここでは、Caption プロパティの値は「設定」され、Text プロパティの値は「取得」されています。
どうやら、プロパティは、単なる変数ではなさそうです。
プロパティ値の変更と共にクラスの他の部分が変化したり、値の変更を許さないプロパティも存在します。
つまり、クラス内部では、プロパティ値の設定や取得を監視していて、状況に応じて裏で対応する操作をしているのです。
クラスにプロパティを追加する場合は、「プロパティプロシージャ」という特別専用のプロシージャを、クラスモジュールに記述します。
具体的には、Property Get と Proerty Let(Set) というプロパティプロシージャがあり、これらをセットで扱います。
Public Property Get propertyname() As type
End Property
Public Property Let propertyname([ByVal|ByRef] newvalue As type)
End Property
これは、プロパティプロシージャのプロトタイプで、propertyname には「プロパティ名」を、type には「プロパティ値の型」を指定します。クラスにプロパティを追加したい場合は、この構文でプロパティプロシージャのセットを用意します。
プロパティプロシージャは、クラス内部で「イベントプロシージャ」のように働きます。(イベントプロシージャとは、イベントが発生したときに呼び出され、実行されるプロシージャのことです。)
Property Get プロシージャは、そのプロパティに対して、外部から「値の取得」が発生したときに呼び出されます。Property Let(Set) は、外部から「値の設定」が発生したときに呼び出されます。
variable = object.property
これは、Property Get が呼び出される場合です。
左辺の変数にプロパティ値が渡される、つまり取得されるときです。
object.property = newvalue
一方、こちらは Property Let が呼び出される構文です。
プロパティ値を、newvalue の値に変更しようとしています。
以下に例として、Name というプロパティを記述してみます。
Public Property Get Name() As String
End Property
Public Property Let Name(ByVal NewValue As String)
End Property
しかし、プロパティプロシージャを用意しただけでは、プロパティの外見を用意しただけで、実際にプロパティとしては動作してくれません。
「プロパティ値」というのですから、元を調べれば、プロパティの実体も「変数」です。当然、クラスモジュールの内部のどこかに、プロパティ値を保存しておく変数を用意するのですが、通常これは、「モジュールレベル変数」として、モジュールの先頭に宣言されます。
Option Explicit
Private m_propertyname As type
モジュールレベルでの変数(プロパティ変数)の宣言です。
プロパティ変数の名前は、プロパティ名と同じにするのが一般的です。type も、プロパティの型と同じですね。
つまり、これがプロパティの実体なのです。
Private スコープで宣言していることに注意してください。
m_ は、変数がモジュールレベルであることを示すプリフィックスです。変数名は自由に付けられますが、一般的に「m_ + プロパティ名」にします。
次のコードは、先ほどの Name プロパティのプロパティ変数の宣言例です。
Option Explicit
Private m_strName As String
値を保存しておく変数とプロパティプロシージャとを結び付けなければなりません。
基本的に、
取得要求のときは、変数を返してあげます。
設定要求のときは、変数の値を変更します。
variable = object.property
Public Property Get Name() As String
Name = m_strName
End Property
Property Get プロシージャは、通常の Function プロシージャと同じです。戻り値として返した値が、左辺の変数に代入されます。プロパティ変数の値を返します。
object.property = newvalue
Public Property Let Name(ByVal NewValue As String)
m_strName = NewValue
End Property
Property Let(Set) プロシージャは、Sub プロシージャと同じ仕組みです。戻り値はなく、代わりに引数を取ります。この引数には、指定された新しい値(右辺)が代入されるので、プロパティ変数にこれを反映します。
内部変数とプロシージャをリンクさせることで、プロパティが正常に動作します。プロシージャ名(Name)と型(String)が一致することで 2 つのプロシージャがセットと見なされ、ひとつのプロパティが追加されるのです。
ここで、Property Let プロシージャを削除すると、Name プロパティは、「値の取得専用」になります。プロパティプロシージャは、「呼び出される」という形で、プロパティ値の制御を行う仕組みになっているのでした。