自身のインスタンスを返すクラス
自分自身のインスタンスを返すクラス
Attribute VB_PredeclaredId = False のとき
クラスモジュールをデフォルトで使うときは、
Attribute VB_PredeclaredId = False
である。
このとき、クラスのメソッドやプロパティは、インスタンス化した後でないと利用できない。
たとえば、次のようなクラスがあったとする。
クラスモジュール HelloWorld
Option Explicit 'Constants' Private Const DEFAULT_MESSAGE As String = "Hello, World!" 'Module Level Variables' Private message_ As String 'Properties' Public Property Get Message() As String If message_ = "" Then _ Message = DEFAULT_MESSAGE: Exit Property Message = message_ End Property Public Property Let Message(ByVal value_ As String) If Len(value_) > 20 Then message_ = "": Exit Property message_ = value_ End Property 'Constructor' Private Sub Class_Initialize() message_ = DEFAULT_MESSAGE End Sub 'Methods' Public Sub sayHello() Call MsgBox(message_) End Sub
無駄に長くて済まない。
Message
というプロパティと、sayHello
というメソッドを持ったクラス。
Message
プロパティはRead/Writeで、デフォルトでは「Hello, World!
」という値になるようにしている。あと、せっかくProperty Let
を使うので、無駄に20字を超えると空文字にするようにしている。
使ってみる
このHelloWorld
クラスを利用してみる。
リスト1 標準モジュール
Public Sub disposable01() HelloWorld.sayHello End Sub
たったこれだけ。インスタンス化せずに使おうとしてみる。既にコード入力時から入力補完も利かないのでダメだろうと予想がつく。実行してみると、
予想通り、そもそもコンパイルが通らず、実行不可。
Attribute VB_PredeclaredId = True のとき
そこで、一旦このHelloWorld.cls
をエクスポートして、エディタで開く。
このように、Attribute VB_PredeclaredId
のところの右辺をTrue
に変えて保存する。
HelloWorld.cls
をインポートして、再度リスト1を実行。
今度は無事実行できた。
ちなみに、次のようにしても同じ結果が出る。
リスト2 標準モジュール
Public Sub disposable01() Dim greeterMan1 As New HelloWorld greeterMan1.sayHello End Sub
つまり、Attribute VB_PredeclaredId = True
のときは、インスタンス化してもしなくてもクラスのメソッド、プロパティが利用可能だということらしい。
自身のインスタンスを返すメソッド
ならば、自身のインスタンスを返すメソッドを内包させることができるはず。
上のHelloWorld
クラスのコードを次のように書き換える。
クラスモジュール HelloWorld
Option Explicit '///Attribute VB_PredeclaredId = True///' 'Constants' Private Const DEFAULT_MESSAGE As String = "Hello, World!" 'Module Level Variables' Private message_ As String 'Properties' Public Property Get Message() As String If message_ = "" Then _ Message = DEFAULT_MESSAGE: Exit Property Message = message_ End Property Public Property Let Message(ByVal value_ As String) If Len(value_) > 20 Then message_ = "": Exit Property message_ = value_ End Property 'Constructor' Private Sub Class_Initialize() message_ = DEFAULT_MESSAGE End Sub 'Methods' Public Sub sayHello() Call MsgBox(message_) End Sub Public Function getInstance( _ ByVal messageString As String) As HelloWorld '……(*)' Dim ret As HelloWorld Set ret = New HelloWorld ret.Message = messageString '……(**)' Set getInstance = ret End Function
冒頭に入れたように、Attribute VB_PredeclaredId
をTrue
にした場合は、コメントで明示しておいた方がいいと思う。
新たに加えたのは(*)のgetInstance
メソッド。
ちょっと気をつけないといけないのは、(**)の部分。
ここを
message_ = messageString
としてしまうと、getInstance
メソッドが返すインスタンスのMessage
プロパティの値がデフォルト値になってしまう。
このカラクリがわからず、しばらくハマってしまった……。
使ってみる
次のコードで実験
リスト3 標準モジュール
Public Sub disposable01() Dim greeterMan2 As HelloWorld Set greeterMan2 = HelloWorld.getInstance("ち~んw") greeterMan2.sayHello End Sub
こいつを実行すると、
ちゃんと引数を渡してインスタンス化したような結果が得られた。
おわりに
何か面白いことに使えないかなあ。