エクセルVBAでクラスのインスタンス生成時に初期データを格納するメソッドを作る方法


みなさん、こんにちは!
タカハシ(@ntakahashi0505)です。

「初心者でもわかるエクセルVBAのクラスモジュール」についてシリーズでお伝えしています。

前回の記事はこちらです。

エクセルVBAでProperty Getプロシージャを使ってプライベート変数にアクセスする方法
「初心者でもわかるエクセルVBAのクラスモジュール」をテーマにシリーズ連載しています。今回はエクセルVBAでPropety Getプロシージャを使ってプライベート変数にアクセスする方法をお伝えします。

Property Getプロシージャによるプライベート変数のプロパティの取得についてお伝えしました。

これで、クラスの作り方とPropertyプロシージャの使い方の超基本部分は抑えられたかなと思います。

が、作ってきたのはエクセル表の1行分を表すクラスです。

複数行をクラスを活かして扱うにはどうしたらよいでしょうか。

ということで、今回からコレクションの特性を持つクラスの作り方を目指していきますので、もうしばらくお付き合いください。

まず今回ですが、その前準備として、エクセルVBAでクラスのインスタンス生成時にデータを簡単に格納するメソッドを作る方法をお伝えします。

では、行ってみましょう!

スポンサーリンク

前回までのおさらい

では、前回までのおさらいからです。

以下のようなエクセル表があります。

エクセルVBAでクラス化するエクセル表

この1行分のデータを表すクラスPersonを作成したのですが、それをクラスモジュールに記述したのが以下のコードです。

Private id_ As String
Public FirstName As String
Public Gender As String
Public Birthday As Date

Public Sub Greet()
    MsgBox Me.FirstName & "です、こんにちは!"
End Sub

Public Property Get IsMale() As Boolean
    IsMale = (Me.Gender = "male")
End Property

Public Property Let Id(ByVal newId As String)
    If id_ <> "" Then
        Debug.Print "Idは上書きすることはできません"
    Else
        id_ = newId
    End If
End Property

Public Property Get Id() As String
    Id = id_
End Property

上記コードにより、クラスに以下のメンバーを追加しています。

  • FirstNameプロパティ、Genderプロパティ、Birthdayプロパティはパブリック変数によるプロパティ
  • Idプロパティはプライベート変数id_についてProperty Letプロシージャ・Property Getプロシージャで設定・取得します
  • IsMaleプロパティはProperty Getプロシージャによる読み取り専用のプロパティです
  • メソッドはGreetという挨拶代わりのメソッドですね

それを検証するための、Subプロシージャは以下の標準モジュールModule1に記載されています。

Sub MySub()

Dim p As Person: Set p = New Person

Dim i As Long: i = 2
With Sheet1
    p.Id = .Cells(i, 1).Value
    p.FirstName = .Cells(i, 2).Value
    p.Gender = .Cells(i, 3).Value
    p.Birthday = .Cells(i, 4).Value
End With

Debug.Print p.Id
'p.Greet

End Sub

これからやりたいこと:複数行のデータをクラスで扱いたい

さて、これで1行分のデータに関してはクラスで扱えるようになりました。

ですが、エクセル表には、Bob以外のPersonもいらっしゃるわけで、それらをクラスとして扱おうと思ったら、以下のように人数分のインスタンスを用意しなければいけない…となると、ちょっとだるいです。

  • エクセル表のデータ行の分だけ以下繰り返す
    1. Personクラスのインスタンスを生成する
    2. インスタンスにエクセル表からデータを取り込む

標準モジュールに、ループ処理を作ってもいいのですが、これ…どうせ必要ならクラスの機能として持っておいたほうがスッキリしますよね。

それで、今回は2の「インスタンスにエクセル表からデータを取り込む」という処理をクラスモジュールPersonの処理として寄せてしまいたいと思います。

インスタンスの初期データをセットするメソッド

VBAのコンストラクタはデータがセットできない

インスタンスの生成時に呼び出される関数コンストラクタと言います。

多くのプログラミング言語のコンストラクタでは、引数を渡すことができ、それをもとに生成したインスタンスのデータの初期化を行えるのです。

VBAでは、Class_Initializeというイベントプロシージャがコンストラクタの役割を果たすのですが、残念ながらこのプロシージャClass_Initializeには、引数を渡すことができません…

なので、インスタンスを生成してから初期データをセットする処理は自前で組む必要があり、実際これまでもそうしてきたわけです。

ですから、どうせインスタンスを生成してから初期データをセットするのであれば、それを楽に行えるメソッドは作っておいて損はないですよね。

インスタンスの初期化をするメソッド

今回、データはエクセル表に展開されています。

ですから、その1行分のデータ範囲つまりRangeオブジェクトを渡したら、それをプロパティにセットしてくれるようにしてくれればシンプルに作れそうです。

クラスモジュールPersonに、以下のようなSubプロシージャInitializeを追加して、Initializeメソッドを作ります。

Public Sub Initialize(ByVal rng As Range)
    id_ = rng(1).Value
    FirstName = rng(2).Value
    Gender = rng(3).Value
    Birthday = rng(4).Value
End Sub

1行分のセル範囲をRangeオブジェクトで受け取って、左上のセルの値から順に、id_、FirstName、Gender、Birthdayにセットをします。

エクセル表の列順さえ入れ替わることがなければ、これでいけそうです。

注記:InitializeメソッドとイベントプロシージャClass_Initializeは別もの

今回作成したInitializeメソッドと、前述のイベントプロシージャClass_Initializeとは別ものですので、混同しないようにお願いします。

Class_Initializeについては、今後のシリーズで紹介をしたいと思います。

Initializeメソッドの動作確認

では、標準モジュールModule1を以下のように変更して、その動作を確認してみましょう。

Sub MySub()

Dim i As Long: i = 2
Dim p As Person: Set p = New Person
With Sheet1
    p.Initialize .Range(.Cells(i, 1), .Cells(i, 4))
End With

Stop
'p.Greet

End Sub

Stopステートメントで中断しているときに、ローカルウィンドウを確認してみると、以下のようにすべてのプロパティが正しくセットされていることが確認できます。

エクセルVBAのインスタンスの初期データを確認する

まとめ

以上、エクセルVBAでクラスのインスタンス生成時にデータを簡単に格納するメソッドを作る方法をお伝えしました。

VBAにはインスタンス生成時にデータを初期化するコンストラクタがないので、その代わりになるメソッドを独自で作成する必要があります。

さて、次回はエクセル表の他のデータも取り扱えるようにコレクションを活用していきます。

エクセルVBAでインスタンスの集合をコレクション化する方法
「初心者でもわかるエクセルVBAのクラスモジュール」をテーマにシリーズ連載をしております。今回は、エクセルVBAでCollectionオブジェクトを使ってインスタンスの集合をコレクション化する方法をお伝えします。

どうぞお楽しみに!

連載目次:初心者でもわかる!エクセルVBAでクラスを作ろう

名前は聞いたことあるけどよくわからない「クラスモジュール」。本シリーズでは、初心者でも少しずつ丁寧にその作り方と便利さについてお伝えしていきますよ!
  1. 【初心者でもできる】エクセルVBAで最も簡単なクラスを作る方法
  2. エクセルVBAでクラスに最も簡単なプロパティを追加する方法
  3. エクセルVBAで自作クラスをインスタンス化する方法
  4. エクセルVBAでクラスに最も簡単なメソッドを追加する方法
  5. エクセルVBAで表の1行分のデータを表すクラスを作成する方法
  6. エクセルVBAでProperty Getプロシージャを使って簡単なプロパティを作成する方法
  7. エクセルVBAでプロパティを他のモジュールからアクセスできないようにする方法
  8. エクセルVBAでProperty Letプロシージャを使ってプロパティ設定をする方法
  9. エクセルVBAでPropety Getプロシージャを使ってプライベート変数にアクセスする方法
  10. エクセルVBAでクラスのインスタンス生成時に初期データを格納するメソッドを作る方法
  11. エクセルVBAでインスタンスの集合をコレクション化する方法
  12. エクセルVBAでコレクション化したインスタンスを取り出す方法
  13. エクセルVBAでクラスモジュールを使って独自のコレクションを作る方法
  14. エクセルVBAでインスタンス生成時に自動で処理を実行するイベントプロシージャClass_Initialize
  15. エクセルVBAで自作コレクションのインスタンス生成時に初期データも投入する方法
  16. エクセルVBAで自作コレクションの要素を取得するプロパティの作り方
  17. エクセルVBAで自作のコレクションに要素を追加するメソッドを作成する
  18. エクセルVBAで自作コレクションの要素を削除するメソッドの作り方
  19. エクセルVBAでエクセル表のデータを反映するメソッドとクラスを使うメリット

コメント

  1. ホイミ より:

    ノンプログラマーです。
    VBAについての記事が非常に分かりやすく、全ての記事をなぞって読んでおります。
    大変ありがたく思います。

    [Module1]内の下記記述について、1点ご質問です。

    p.Initialize .Range(.Cells(i, 1), .Cells(i, 4))

    これは、下記のように、実引数を括弧で囲わないところがなぜか分かりませんでした。

    p.Initialize(.Range(.Cells(i, 1), .Cells(i, 4)))

    ご教授いただければ幸いです。

    • ホイミさん

      以下コードですが、VBAでは呼び出したメソッドで戻り値を使用しない場合は、引数にかっこを付与しないというルールになっているんです。

      p.Initialize .Range(.Cells(i, 1), .Cells(i, 4))
      

      InitializeメソッドはSubプロシージャでできていますから、もともと戻り値を返す機能もないですしね。

      例えば、InitializeメソッドがFunctionプロシージャでできていて、その戻り値を変数とかに格納して使いたい場合は、以下のようにかっこを付与する必要があります。

      hoge = p.Initialize(.Range(.Cells(i, 1), .Cells(i, 4)))
      

      戻り値を使用するかどうかで、引数をかっこで囲むかどうかが変わるんです。VBAの構文のいけてないとこの一つですね。

  2. ホイミ より:

    ご親切にご返答ありがとうございます。
    内容、納得しました。

    VBAについて新連載をはじめ、今後の記事も楽しみにしております。

タイトルとURLをコピーしました