はじめに断っておくと、実際にVBAのコンストラクタに引数を渡す裏技は無い。
タイトル詐欺で申し訳ない。
2015/2/15追記
これより後の記事で、もう少しスマートなやり方を発見している。(以下リンク)
VBAでインターフェースを使って引数付きのコンストラクタを実現する。 - t-hom’s diary
しかし、私がVBAでコンストラクタを使いたい理由は、値を一度だけ設定させてそれ以降の変更を禁止したいというのが目的であり、擬似的にこれを実現させることは成功したので紹介する。
以下のコードを新しいクラスモジュール(仮にSampleClassとする)に貼り付ける。
Option Explicit Private Initialized As Boolean '初期値はFalse Private val As String '初期値は空文字 Enum ErrCode 'エラーNo.0~512はシステムエラー用に予約されている。 InitTwice = 513 UnInited = 514 End Enum Public Sub Init(ByRef v As String) 'val値で初期化有無を判断せずにわざわざInitializedフラグを用意するのは、 '空文字かどうかで判定させると空文字でInitできなくなるから。 If Not Initialized Then val = v Initialized = True Else Call RaiseError(ErrCode.InitTwice) End If End Sub Property Get Value() As String '各メソッドでUnInitCheckを呼び出すことで初期化忘れをさせない。 Call UnInitCheck Value = val End Property Private Sub UnInitCheck() '初期化されていなければエラーを呼び出すメソッド 'たった3行ではあるが、メソッドが増えるとメンテが面倒なので '別メソッドに分離した。 If Not Initialized Then Call RaiseError(ErrCode.UnInited) End If End Sub Private Sub RaiseError(ByRef code As Long) Dim message As String Select Case code Case ErrCode.InitTwice message = "すでに初期化されています。Initは一度しか呼び出しできません。" Case ErrCode.UnInited message = "最初にInitメソッドでクラスを初期化してください。" Case Else message = "不明なエラーが発生しました。" End Select 'エラーのSourceには一応クラス名を書いておく。 '(活用されるシチュエーションは思いつかないが) 'TypeName関数にMeを渡せばでクラス名が取れる。 'VBAにリフレクションがあったことに驚いた。(しょぼいけど) Err.Raise _ Number:=code, _ Source:=TypeName(Me), _ Description:=TypeName(Me) & ": " & message End Sub
これを標準モジュールから呼び出す。
Sub test() Dim x As New SampleClass x.Init ("あいうえお") Debug.Print x.Value x.Init ("かきくけこ") Debug.Print x.Value End Sub
すると、
無事にエラー発生
Init前にValueプロパティを取ろうとすると別のエラーが発生する。
クラスは一度作ったら何度も使うので、面倒でも堅牢に実装しておきたい。