t-hom’s diary

主にVBAネタを扱っているブログ…とも言えなくなってきたこの頃。

VBAでClass_Initializeに引数を渡したい。

はじめに断っておくと、実際に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

すると、
f:id:t-hom:20150208121542p:plain
無事にエラー発生

Init前にValueプロパティを取ろうとすると別のエラーが発生する。

クラスは一度作ったら何度も使うので、面倒でも堅牢に実装しておきたい。

当ブログは、amazon.co.jpを宣伝しリンクすることによってサイトが紹介料を獲得できる手段を提供することを目的に設定されたアフィリエイト宣伝プログラムである、 Amazonアソシエイト・プログラムの参加者です。