クラス変数っぽいものを実現してみる
Attribute VB_PredeclaredIdをTrueにしたクラスモジュールでちょっと実験をしてみた。
実験用クラス
実験用に、Rahmenクラスを作成。
こちらの記事にインスパイヤされたでござる。
クラスモジュール Rahmen
Option Explicit '///Attribute VB_PredeclaredId = True///' Public Enum NoodleSolidity nsYugeTooshi nsKonaOtoshi nsHarigane nsBariKata nsKata nsFutsuu nsYawa nsZundare End Enum Private count_ As Long Private solidity_ As String Public Property Get Solidity() As String Solidity = solidity_ End Property Public Property Let Count(ByVal value_ As Long) count_ = value_ End Property Public Property Get Kaedama() As String Kaedama = CStr(count_) & "玉目" End Property Private Sub Class_Initialize() solidity_ = "ふつう" End Sub Public Sub setSolidity(ByVal solidityArg As NoodleSolidity) solidity_ = getSolidityString(solidityArg) End Sub Private Function getSolidityString( _ ByVal arg As NoodleSolidity) As String Dim ret As String Select Case arg Case nsYugeTooshi: ret = "湯気とおし" Case nsKonaOtoshi: ret = "粉落とし" Case nsHarigane: ret = "ハリガネ" Case nsBariKata: ret = "バリカタ" Case nsKata: ret = "カタ" Case nsFutsuu: ret = "ふつう" Case nsYawa: ret = "やわ" Case nsZundare: ret = "ずんだれ" Case Else: ret = "ち~んw" End Select getSolidityString = ret End Function Public Function getInstance( _ ByVal solidity__ As NoodleSolidity) As Rahmen Dim ret As New Rahmen Call ret.setSolidity(solidity__) count_ = count_ + 1 ret.Count = count_ '……(*)' Set getInstance = ret End Function
例によって、Attribute VB_PredeclaredIdをTrueにしている。
NoodleSolidityという列挙体をPublicで宣言しているので、このクラスモジュールをインポートしたプロジェクトでは、この列挙体を使用することができる。
基本的に、getInstanceメソッド内でsetSolidityメソッドに渡すための引数に用いる。
setSolidityメソッドからは、getSolidityStringメソッドを呼んで、〈麺のかたさ〉を表す文字列を取得する。一番かたい「湯気とおし」から一番やわらかい「ずんだれ」まで、無駄にヴァリエーション豊富にしたw
Solidityプロパティ
変数solidity_の内容を返すだけ。
変数solidity_に値をセットするのは、デフォルトのコンストラクタであるClass_Initializeか、インスタンスメソッドのsetSolidity(とそこから呼ばれるgetSolidityStringメソッド)のみ。
setSolidityにNoodleSolidity列挙体のメンバを引数として渡すと、対応する〈麺のかたさ〉を表す文字列を取得して変数solidity_にセットする。
Kaedamaプロパティ
変数count_に入っている数値を文字列に変換し、「玉目」を附加した文字列を返す。
実は、getInstanceメソッドをRahmen.getInstanceの形で呼び出したとき、メソッド内部の変数count_には、Rahmen内部の(インスタンスのものでない)変数count_の値が入っている。
この性質を生かして、クラス変数的なことができないものか、と考えたわけです。
そこで、getInstanceメソッドの(*)のところ、
ret.Count = count_
とした。
こうすることで、Rahmenが持っているcount_の数値をインスタンスが持っている変数count_にセットするのだ。
逆にいえば、retのCountプロパティにセットする、という形にしないと、インスタンス内部のcount_を変更できないらしい。
実は、このことが大問題の原因になる。後述する。
とりあえず使ってみる
標準モジュールに次のコードを書いて使ってみる。
リスト1 標準モジュール
Public Sub disposable01()
Dim rahmen1 As Rahmen
Set rahmen1 = Rahmen.getInstance(nsKonaOtoshi)
Dim rahmen2 As Rahmen
Set rahmen2 = Rahmen.getInstance(nsBariKata)
Dim rahmen3 As Rahmen
Set rahmen3 = Rahmen.getInstance(nsZundare)
Call printData(rahmen1)
Call printData(rahmen2)
Call printData(rahmen3)
Debug.Print Rahmen.Kaedama
End Sub
Private Sub printData(ByVal targetRahmen As Rahmen)
With targetRahmen
Debug.Print "替え玉:" & .Kaedama; _
"/麺のかたさ:" & .Solidity
End With
End Sub
見ておわかりのとおり、rahmen1、rahmen2、rahmen3の三つのRahmen型変数を準備し、それぞれRahmen.getInstanceメソッドでインスタンス化する。
あとは、printDataメソッドを用いて、それぞれのKaedamaプロパティとSolidityプロパティ、すなわち〈麺のかたさ〉を表示し、最後にRahmenオブジェクト自体のKaedamaプロパティを表示しておしまい。
実行結果
一回目。

二回目。

一応、意図どおりの結果が出ている。
クラス内の変数count_が、クラス変数のような働きをしている。
大問題
〈麺のかたさ〉を設定するsetSolidityメソッドはともかく、Property Let CountがPublicだというのは非常にまずい。
count_が外部から自在に書き換えられてしまうからである。
あと、setSolidityメソッドにしても、単独でこのメソッドを実行するだけなら、Rahmen内部の変数count_は変化しないので、おかしなことになる。
おわりに
まだまだ解決せねばならんことが多い……。