タイトルにコンストラクタとあるが、正確にはコンストラクタもどきである。
先日VBAのAriawaseライブラリのCore.basに書かれたInitのコードに感銘を受けた話を書いた。
ただ、私の個人的な好みとしては、外部DLLの参照よりはVBAの基本機能だけで解決してしまいたい。
そこで、Initializableインターフェースを作って対処してみた。
こちらはクラスモジュール:Initializableのコード
Public Function Init(p() As Variant) As Object End Function
次に、Initメソッドを実装するクラスモジュール例:SheetExのコード
SheetExは、Worksheetのラッパーとして作った。
現時点ではMaxRow,MaxColumnしか無いが、いろいろ拡張できるはず。
(アイデアが無いだけ)
Implements Initializable Dim a_self As Worksheet Private Ready As Boolean Enum ErrCode 'エラーNo.0~512はシステムエラー用に予約されている。 InitTwice = 513 NotReady = 514 End Enum Private Sub ReadyCheck() If Not Ready Then Err.Raise ErrCode.NotReady, _ TypeName(Me), "クラス「" & TypeName(Me) & "」は初期化されていません。" End Sub Property Get Self() As Worksheet ReadyCheck Set Self = a_self End Property Private Function Initializable_Init(p() As Variant) As Object If Ready Then Err.Raise ErrCode.InitTwice, TypeName(Me), _ "クラス「" & TypeName(Me) & "」はすでに初期化されています。" Set a_self = p(0) Set Initializable_Init = Me Ready = True End Function Property Get MaxRow() As Long ReadyCheck MaxRow = a_self.UsedRange.Find("*", , xlFormulas, , xlByRows, xlPrevious).Row End Property Property Get MaxColumn() As Long ReadyCheck MaxColumn = a_self.UsedRange.Find("*", , xlFormulas, , xlByColumns, xlPrevious).Column End Property
このSheetExは、コンストラクタであるInitメソッドでWorksheetオブジェクトを受け取ってa_selfに格納する。MaxRowやMaxColumnを取得するメソッドを追加している。
※シート自体を取り出すのに、Selfメソッドが必要な部分はイケてないがデフォルトメソッドの設定が出来なかったので妥協。
次に、ObjectのInitメソッドを呼び出す汎用Init関数
これは標準モジュールに記載するが、Ariawaseを使用している場合は関数名が被るので要変更。
Function Init(o As Initializable, ParamArray p()) As Object 'ParamArrayで受け取った配列pは、 'なぜかそのまま別関数の引数に配列として渡すことが出来ない。 'そのため、面倒だが一旦別配列p2に格納している。 Dim p2() As Variant ReDim p2(UBound(p)) For i = 0 To UBound(p) If IsObject(p(i)) Then Set p2(i) = p(i) Else Let p2(i) = p(i) End If Next Set Init = o.Init(p2) End Function
Init関数の引数をInitializable型にしているのが、汎用化の肝である。
これで、Initializableインターフェースを実装しているクラスであればこのInit関数で初期化ができる。
つまり、Ariawaseと同じように、引数付きコンストラクタもどきができる。
Sub test() Dim x As SheetEx Set x = Init(New SheetEx, Sheet1) Debug.Print x.Self.Name Debug.Print x.MaxRow, x.MaxColumn End Sub
難点は、インターフェースのためにひとつクラスモジュールが増えるところ。
このあたりがVBAのイケてない点であるが、元々クラスモジュールを多様するコードというのはAriawase以外にお目にかかったことが無いので邪魔になるほどではないと思う。