クラス変数っぽいものを実現してみる

 

クラス変数っぽいものを実現してみる

Attribute VB_PredeclaredIdTrueにしたクラスモジュールでちょっと実験をしてみた。

実験用クラス

実験用に、Rahmenクラスを作成。

infoment.hatenablog.com

こちらの記事にインスパイヤされたでござる。

クラスモジュール 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_PredeclaredIdTrueにしている。

NoodleSolidityという列挙体をPublicで宣言しているので、このクラスモジュールをインポートしたプロジェクトでは、この列挙体を使用することができる。

基本的に、getInstanceメソッド内でsetSolidityメソッドに渡すための引数に用いる。

setSolidityメソッドからは、getSolidityStringメソッドを呼んで、〈麺のかたさ〉を表す文字列を取得する。一番かたい「湯気とおし」から一番やわらかい「ずんだれ」まで、無駄にヴァリエーション豊富にしたw

Solidityプロパティ

変数solidity_の内容を返すだけ。

変数solidity_に値をセットするのは、デフォルトのコンストラクタであるClass_Initializeか、インスタンスメソッドのsetSolidity(とそこから呼ばれるgetSolidityStringメソッド)のみ。

setSolidityNoodleSolidity列挙体のメンバを引数として渡すと、対応する〈麺のかたさ〉を表す文字列を取得して変数solidity_にセットする。

Kaedamaプロパティ

変数count_に入っている数値を文字列に変換し、「玉目」を附加した文字列を返す。

実は、getInstanceメソッドをRahmen.getInstanceの形で呼び出したとき、メソッド内部の変数count_には、Rahmen内部の(インスタンスのものでない)変数count_の値が入っている。

この性質を生かして、クラス変数的なことができないものか、と考えたわけです。

そこで、getInstanceメソッドの(*)のところ、

ret.Count = count_

とした。

こうすることで、Rahmenが持っているcount_の数値をインスタンスが持っている変数count_にセットするのだ。

逆にいえば、retCountプロパティにセットする、という形にしないと、インスタンス内部の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

見ておわかりのとおり、rahmen1rahmen2rahmen3の三つのRahmen型変数を準備し、それぞれRahmen.getInstanceメソッドでインスタンス化する。

あとは、printDataメソッドを用いて、それぞれのKaedamaプロパティとSolidityプロパティ、すなわち〈麺のかたさ〉を表示し、最後にRahmenオブジェクト自体のKaedamaプロパティを表示しておしまい。

実行結果

一回目。

f:id:akashi_keirin:20190502204015j:plain

二回目。

f:id:akashi_keirin:20190502204018j:plain

一応、意図どおりの結果が出ている。

クラス内の変数count_が、クラス変数のような働きをしている。

大問題

〈麺のかたさ〉を設定するsetSolidityメソッドはともかく、Property Let CountPublicだというのは非常にまずい。

count_が外部から自在に書き換えられてしまうからである。

あと、setSolidityメソッドにしても、単独でこのメソッドを実行するだけなら、Rahmen内部の変数count_は変化しないので、おかしなことになる。

おわりに

まだまだ解決せねばならんことが多い……。