本ページには広告が含まれています。

Excel VBA クラス コンストラクタを利用する方法(Lv.12)

Excel VBA クラスのコンストラクタをうまく利用する方法
画像はCanva「Text to Image」で生成しました

「Excel VBAのクラス」について第12回目です。VBAの「コンストラクタ」は引数を渡すことができないという残念な仕様なので、何とかできないか検討してみたいと思います。

くるみこ
くるみこ

前回「コンストラクタ」と「デストラクタ」について簡単に学びました。「コンストラクタ」に引数を渡せないことがわかったので、今回はそれを代替できる案についてです(^^)

わかりました。「コンストラクタ」をうまく利用できるようにするんですね。

今回もよろしくお願いしますm(_ _)m

スポンサーリンク

はじめに

クラスをインスタンス化(New)したときに実行させるメソッドが「コンストラクタ」です。

Class_Initialize()というプロシージャ内に、インスタンス化の際に実行したいコードを書いておけば自動的に処理されるようになっています。

ただし、VBAの場合引数を渡すことができない仕様なのでそれに代わる対処法を考えていきます。

【この記事でわかること
・Excel VBA のクラスで「コンストラクタ」をうまく利用する方法について

使用している表データは前回のままです。

エクセルシートの表データ

Class_Initialize内でクラスをNewする

Class_Initialize() の動作についていろいろ動かして確認してみたことを書きます。

同一クラスのインスタンス作成はダメでした

次のコードをご覧ください。

' Class1のコンストラクタ
Private Sub Class_Initialize()
  Dim obj As New Class1
End Sub

このコードは、Class1 の Class_Initializeイベントです。Class1をNewしています。

この場合、Class1 の Class_Initializeイベントが再帰的に呼び出されて無限ループになります。

これは使えません。注意しましょう。

Class_Initialize内で別のクラスをNewする

Class1 の Class_Initializeイベント内で、別のクラスのインスタンスはNewできます。

' Class1のコンストラクタ
Private Sub Class_Initialize()
    Dim obj As New Class2
End Sub

このコード(別クラスのインスタンス化)なら問題ありません。

ということは、Class2Class_Initializeイベント内にうまく処理を書けば何か出来そうですね。

あとでこの部分を使っていきたいと思います。

【検討①】コンストラクタで何をさせたいのか

前々回「第10回目」でインスタンスをコレクション化しました。この部分をそっくりコンストラクタと同時に全部終わらせてしまうことが出来ないか検討したいと思います。

ここで使ったコード例を手直ししながらやっていきます。

プロパティ値の設定部分を引数付きメソッドにする

クラスのメソッドを作成して、そこでプロパティの値を取得するようにしたいと思います。

引数には、シートの表データをRangeオブジェクトで渡すように設定します。

クラス側に設定した2つのメソッド(Function)がこちらです。

'ownerのコンストラクタ直後に各プロパティを設定用
Public Function Init1(ByVal rng As Range) As Class1
  Set Init1 = Me '自分自身を返す
  With Me
    .ID = rng(1).Value    'IDに代入
    .Name = rng(2).Value  'Nameに代入
    .Age = rng(3).Value   'Ageに代入
  End With
End Function
'owner.Petのコンストラクタ直後に各プロパティを設定用
Public Function Init2(ByVal rng As Range) As Class1
  Set Init2 = Me '自分自身を返す
  With Me
    .Name = rng(4).Value   'owner.PetのNameに代入
    .Age = rng(5).Value    'owner.PetのAgeに代入
    .Types = rng(6).Value  'owner.PetのTipesに代入
    .Gender = rng(7).Value 'owner.PetのGenderに代入
End With
End Function

元の標準モジュールからコードを移植しています。

Rangeオブジェクトにしないと、引数で渡すには都合が悪いのでその部分は変更しています。

コンストラクタ直後にメソッド実行

標準モジュール側のコードがこちらです。

Option Explicit
'標準モジュール
Sub rngCollectionTest()
  '変数宣言
  Dim owner As Class1 'ownerクラスインスタンス用
  Dim rng As Range    'Rangeオブジェクト用
  'コレクションオブジェクト作成
  Dim mycol As Collection: Set mycol = New Collection
  Dim i As Long: i = 4 'ループ処理用:初期値4
  With Sheet1
    Do While .Cells(i, 1).Value <> ""
      Set rng = .Range(.Cells(i, 1), .Cells(i, 7))
      'インスタンス作成:引数付きメソッドで値代入
      Set owner = New Class1: owner.Init1 rng
      Set owner.Pet = New Class1: owner.Pet.Init2 rng
      'コレクションに書き込む
      mycol.Add owner, owner.ID
      i = i + 1
    Loop
  End With
  '確認用にメッセージを表示
  For i = 4 To i - 1
    With mycol.Item(i - 3)
      MsgBox "OWNER:" & .Name & "(" & .Age & "歳)" _
        & vbCrLf & "ペット名:" & .Pet.Name & _
        "(" & .Pet.Age & "歳)" & .Pet.Types & _
        "/" & .Pet.Gender
    End With
  Next
  Set mycol = Nothing 'コレクション開放
End Sub

前回と変更している部分のみ解説します。

・12行目が表の1行分データの範囲をRangeオブジェクトに設定してる部分です。
 Rangeオブジェクトにすることでインデックスで管理できます。

・14行目の ownerインスタンス化でコンストラクタが発生します。直後に「:」をはさみ
 Init1メソッドを引数 Rng を渡して実行します。

・15行目は、14行目と同様にコンストラクタ直後に Init2メソッドを引数 Rng 付きで実行します。

・22~28行目では(前回は格納直後に表示していましたが)コレクションに格納したデータを表示確認しています。ちゃんと正しく格納できているかここで確認しています。

・30行目、コレクションオブジェクトを開放しています。解放しておかないとオブジェクトが残ってしまいますので注意しましょう。

コレクション開放でインスタンスのデストラクタが発生する

コレクションオブジェクトを先ほどの30行目で解放していますが、解放と同時にコレクション内に格納されているインスタンスも解放されます

インスタンスの解放を確認するため、デストラクタ(Class_Terminate)部分にメッセージを表示するようにして検証してみました。すると、今回は3行のデータを2つのインスタンスで分担していたので、6回(3×2=6)デストラクタが発生していました。

行数が多くなるとその分増えるというわけですね。

コレクションは標準モジュールに作成されている

実は、この方法ではコレクションが標準モジュールで宣言されています。この場合、再利用するにはコレクション内のオブジェクトがどのような型であるかを明示的に指定する必要がでてきます。

VBAのコレクションにクラスのインスタンスを格納する場合、宣言する場所はクラスモジュール内で宣言することが一般的のようです。

クラスモジュール内で宣言すれば、コレクションに格納されたオブジェクトが、そのクラスのインスタンスであることが明確ですし、どのようなプロパティやメソッドを持っているかも明確になっているというわけです。

ということで、次にコレクションをクラスで宣言するように変更していきます。

【検討②】クラスでコレクションを処理する

まず、やりたいことをザックリ書いてみます。

Class2をインスタンス化(NEW) → Class_Initialize → コレクション生成 → Class1のインスタンス化 → プロパティ値取得 → コレクションに格納 → すべて処理したら → コレクション破棄 → Class1のインスタンス破棄(Class_Terminate)→ Class2のインスタンス破棄(Class_Terminate)

こんな感じでしょうか。ではこれを反映するように設定してみます。

呼び出し用のクラスモジュールを追加

クラスモジュールを追加して「clsCol」という名前にしました。

コードはこちらです。前の標準モジュールの部分を移してきた感じです。

Option Explicit
'コレクション用変数
Public mycol As Collection
'コンストラクタ
Private Sub Class_Initialize()
  '変数宣言
  Dim owner As Class1 '別クラス呼び出し用
  Dim rng As Range
  Dim i As Long: i = 4
  'コレクション初期化
  If mycol Is Nothing Then Set mycol = New Collection
  'Sheet1の表データからプロパティ値取得
  With Sheet1
    Do While .Cells(i, 1).Value <> ""
      Set rng = .Range(.Cells(i, 1), .Cells(i, 7))
      Set owner = New Class1: owner.Init1 rng
      Set owner.Pet = New Class1: owner.Pet.Init2 rng
      'コレクションに書き込む
      mycol.Add owner, owner.ID
      i = i + 1
    Loop
  End With
End Sub
'デストラクタ
Private Sub Class_Terminate()
  'ここでコレクションオブジェクトを解放
  If Not (mycol Is Nothing) Then Set mycol = Nothing
End Sub

・Rangeオブジェクトを使うように修正しています。

・11行目、クラス内でコレクションオブジェクトを生成しています。

・16行目と17行目が、コンストラクタ「Class_Initialize()」内で別クラスをインスタンス化している部分です。直後にメソッドにRangeオブジェクトの引数を付けて実行させています。

・25行目からのデストラクタ「Class_Terminate()」内でコレクションを開放しています。

標準モジュールがこちら

確認用のメッセージ部分を消すと標準モジュールはこれだけになりました。

Option Explicit
'標準モジュール
Sub rngCollectionTest()
  Dim table As clsCol
  'インスタンス作成⇒コンストラクタ起動
  Set table = New clsCol
End Sub

・なんでも良いのですが「table」という名前のインスタンスをNewしています。

・たったこれだけになってスッキリしました。

ローカルウィンドウでコレクションを確認します

最後にローカルウィンドウでコレクション「myCol」を確認します。
※ VBEメニューから表示(V)→ローカルウィンドウ(S)クリックで表示できます。

ローカルウィンドウでコレクションを確認

「clsCol」クラスの「table」インスタンスに作成されていることが確認できました。

まとめ(おわりに)

以上、Excel VBAのクラスで引数を渡すことが出来ない「コンストラクタ」をうまく利用する方法について紹介しました。

くるみこ
くるみこ

クラスの12回目はいかがでしたか。

引数を渡せない「コンストラクタ」の動作について利用方法をいろいろ検討してみましたが、今回紹介した方法が解決策の一つになるのではないでしょうか。
次回はコレクション化したインスタンスからデータを取り出す方法について勉強しましょう。

コンストラクタ内でNewできるケースを知ることが出来てよかったです。

コレクションについても勉強になりました(^^)
次回もよろしくお願いしますm(_ _)m

まとめ

最後に、今回勉強した内容を整理しておきましょう。

Excel VBA クラスの「コンストラクタ」をうまく利用する方法について

【コンストラクタ】内では、同一クラスのインスタンスはNewできませんが、別のクラスをNewすることはできます。

・プロパティ値を設定するための引数付き「メソッド」を作成してNew直後に実行すれば引数を渡せない「コンストラクタ」の代替になります。

・コレクションにクラスのインスタンスを格納する場合、クラスモジュール内で宣言するようにしましょう。

次回は、コレクション化したインスタンスを取り出す方法を勉強します。よろしければ引き続きご覧ください。

Excel VBA クラスについての記事一覧

Excel VBA クラスの作成・使い方について(Lv.1)
Excel VBA クラスのプロパティ設定について(Lv.2)
Excel VBA クラスにメソッドを設定する方法(Lv.3)
Excel VBA クラスをインスタンス化する方法(Lv.4)
Excel VBA クラス Propertyプロシージャ(Lv.5)
Excel VBA クラス Property Getプロシージャ(Lv.6)
Excel VBA クラス Property Letプロシージャ(Lv.7)
Excel VBA クラス Property Setプロシージャ(Lv.8)
Excel VBA シートの表データからクラスを作成 (Lv.9)
Excel VBA クラス インスタンスをコレクション化(Lv.10)
Excel VBA クラス コンストラクタとデストラクタ(Lv.11)
Excel VBA クラス コンストラクタを利用する方法(Lv.12)
Excel VBA クラス コレクションの要素取得方法(Lv.13)
Excel VBA クラス コレクション要素の追加と削除(Lv.14)
Excel VBA クラス コレクションの内容を書き出す(Lv.15)
Excel VBA クラス UserFormで機能を操作する(Lv.16)
Excel VBA クラス コレクションの最大要素数について(Lv.17)

★★★ ランキング参加中! クリックしてね(^^)/ ★★★

スポンサーリンク
スポンサーリンク

過去記事のサンプルファイルをダウンロードできます

この記事のサンプル登録はありません。記事内のコードをご利用ください。

過去の記事で使用したサンプルファイルがダウンロードできるページを設置しています
こちら(このリンク先)からご利用ください