最近になってやっとVBAのクラスモジュールの使い方がわかってきた感じがします。少し前からVBAでツール開発する仕事においてクラスモジュールの必要性というのは感じていました。
フォームコントロールやActiveXコントロールを使う場合、2個や3個ぐらいの範囲内であればそのコントロールごとにコードを記述していっても問題ありません。
しかしこれが10個や100個、もしくは状況によって変動するようなツールだと手間や柔軟性などを考慮するとコントロールごとにコードを記述するわけにはいかなくなってきます。
また、ExcelやAccessに最初からあるイベントだけでは対応できない場合にも「新しいイベント」をつくるためにクラスモジュールの知識が必要になってきます。
こういった状況があったにも関わらず、VBAのクラスモジュールについてどうしてもいまいち掴みきれずにいました。
けれども、最近になってやっと「こういうことかな」と思えるようになってきたので、自分の理解や備忘録のためにもクラスモジュールの最初の部分について簡単にまとめてみます。
VBAにおけるクラスモジュールとは、クラスとは
経理や事務的な仕事において、現在日本の多くの会社ではExcelが使われています。そのExcelにはVBAというプログラミング言語が標準で搭載されていますが、この機能の中のひとつに「クラスモジュール」というものがあります。
ではクラスモジュールとはどういったものかというと、『VBAエキスパート公式テキスト Access VBA スタンダード』には次のようにかかれています。
P.142
クラスモジュールとは
独自のオブジェクトを利用するためには、次の2つの手順が必要です。
①オブジェクトの動作を設計する手順
②設計したオブジェクトをプログラムの中で利用する手順
オブジェクトを設計するためのモジュールがクラスモジュールです。また設計したオブジェクトをプログラムの中で利用することを、インスタンスを生成する(実体化する)といいます。(後略)
他の書籍では、クラスモジュールの「クラス」は「オブジェクトの設計図」という記述もあります。
クラスモジュールを作っただけでは使うことはできません。しかし設計図(クラス)を元にインスタンシング(実体化)することで実際のプログラムの中で使えるようになる、というのが一般的に言われている説明になります。
今だからこそ少しずつわかってきましたが、最初この文章を読んだ時は何を言っているのかわかりませんでした。いや、自分が何をわかっていないのかがわかっていなかったというレベルでした。
この部分についてもう少し詳しく説明してきます。
VBAでクラスモジュールを使う場合の考え方
言葉であれこれ説明するよりも実際のコードを使って説明していった方がわかりやすいかと思います。
『入門レベルでは決して足りない実務に必須のスキルとは ExcelVBA 実戦のための技術』の中で、クラスモジュールに記述するコードの一例として次のように書かれている部分があります。
P.304
Sample09-2.xlsm Student
Private mName As String ’氏名
Private mAge As Long ‘年齢
Private mMobile As String ‘電話番号
‘氏名を設定する
Public Property Let Name(ByVal vName As String)
mName=vName
End Property
‘氏名を返す
Public Property Get Name() As String
Name=mName
End Propety
(後略)
引用文の「Sample09-2.xlsm Student」の中の
- 「Sample09-2.xlsm」がファイル名
- 「Student」がモジュール名(クラスモジュール)
になります。
引用したクラスモジュールを使うために、本書にはさらに次のようなコードが書かれています。
P.307
Sample09-2.xlsm Module1
Private Sub UseClassModule()
’Studentクラスのオブジェクトを代入する変数
Dim vStudent As Student
’Studentクラスのオブジェクトを生成する
Set vStudent =New Student
’生徒の情報を設定する
vStudent.Name=”羽生 健太郎”
vStudent.Age=17
vStudent.Mobile=”xxx-xxxxx-xxx”
’生徒の情報を出力する
Debug.Print vStudent.Name
Debug.Print vStudent.Age
Debug.Print vStudent.Mobile
End Sub
引用文の「Sample09-2.xlsm Module1」の中の
- 「Sample09-2.xlsm」がファイル名
- 「Module1」がモジュール名(標準モジュール)
になります。
自分の今までのクラスモジュールの理解を説明するために便宜的に以上のコードを引用してみました。
2つ目のコードで「Dim vStudent As Student」と「Set vStudent =New Student」と記述されている部分があります。
今までは、この「Student」がクラスモジュールの「名前」であると同時に、vStudentにクラスモジュールであるStudentが格納されたオブジェクト変数である、ということを理解できていませんでした。
「今までは」という言葉を使いましたが、よりわかりやすく説明するために以下のようなざっくりとしたクラスモジュールの概念図をつくってみました。
○今までの自分の考え方
以上の概念図が今までの自分のクラスモジュールの理解でした。もう少し説明すると、薄い水色の部分が1つのクラスモジュール全体の領域、内側の青い部分がクラスモジュール内で記述される様々なプロシージャなどの領域です。
クラスモジュールを正しく使えるようにするためには、以下のような考え方を理解していないといけない、ということがわかってきました。
○正しい考え方
上の図と下の図で何が違うのかというと、上の図はクラスモジュール内にあるプロシージャに直接アクセスしようとしています。一方下の図では、まず上の階層であるクラスモジュールにアクセスしてからその下の階層であるプロシージャにアクセスしています。
要はアクセスする順番が違っているということです。
『入門レベルでは決して足りない実務に必須のスキルとは ExcelVBA 実戦のための技術』から引用文でコード的に表すと次のようになります。
今までの理解 | Name=”AAA” |
現在の理解 | vStudent.Name=”AAA” |
「Name=”AAA”」だけでは何のオブジェクトの名前に「AAA」を格納したいのかコンピュータにとってはわかりません。これでも機能することは機能するのですが、単純に「Name」という変数に「AAA」という文字列が格納されるだけになってしまいます。
これでは、特定のクラスモジュール内にあるNameプロパティに「AAA」という文字列を格納することは出来ません。
一方「vStudent.Name=”AAA”」であればvStudentクラスモジュールの中のNameプロパティに「AAA」を格納することができます。
Name=”AAA”のようにクラスモジュールのプロパティプロシージャに直接アクセスしようとするのではなく、まず「vStudent.」という風にクラスモジュールという「オブジェクトにアクセス」してから次にその内部にあるプロパティプロシージャにアクセスしないとクラスモジュールは操作できないということです。
なぜクラスモジュールの使い方を理解できなかったのか
ここまでクラスモジュールの使い方を書いてきましたが、重要なのは最初にまず「クラスモジュール」というオブジェクトにアクセスすることです。
では、自分は今までなぜこういったことがなかなか理解できなかったのか考えた所、おそらく「Callステートメント」にあるのではないかと思いました。
VBAにおけるCallステートメントは『VBAエキスパート公式テキスト Excel VBA スタンダード』には次のように書かれています。
P.28
モジュール内に存在する別のプロシージャを呼び出すには、Callという命令を使います。次のマクロ「Sample1」は、セルA1「100」を入力した後で、プロシージャ「Sample2」を呼び出します。
Sub Sample1()
Range(“A1”).Value=100
Call Sample2
End Sub
Sub Sample2()
Ramge(“B1”).Value=Range(“A1”).Value*2
End Sub
引用した記述内のコードをの説明ですがSample1を実行すると、まずA1セルに「100」が入力されます。
次に「Call Sample2」が実行されてSample2プロシージャの内容が実行されます。これによってA1セルの2倍した値がB1セルに入力されることになります。
このようにCallステートメントは非常に便利で、特定のプロシージャから外部のプロシージャを呼び出して実行できます。この方法の良い所は1つのプロシージャから他のいろんなプロシージャを呼び出すことが出来ます。
別の言い方をすれば、特定の処理を別の様々なプロシージャから使いまわせるとも言えますし、プログラミングの幅も大きく広がります。
ポイントは、このCallステートメントを使うと通常は直接プロシージャを呼び出すという方法をとります。自分にとっては、この機能、やり方が1つ上の階層にある「オブジェクト」という発想を持ちにくくしてしまった、意識しづらくしてしまったのかもしれません。
この部分を理解することによって、クラスモジュールの中の「Propertyプロシージャ」とか「Getキーワード」「Letキーワード」などの使い方が点と点が繋がってきたというか、少しずつわかってきました。
『入門レベルでは決して足りない実務に必須のスキルとは ExcelVBA 実戦のための技術』のクラスモジュールの章を読んでいて何となく違和感を感じた時がありました。
それは「なんでモジュール名がこんなに出てくるんだろう」と。
今だから「あぁ、それはクラスモジュールを操作するのに必要なんだな」とわかりますが、そういったことがわかる前は、違和感というか、何がわからないのかがわからない、という感じでした。
そもそも普通にVBAを使っているだけでは、標準モジュールとかシートモジュール、ブックモジュールに名前を付ける機会がないというか、別にそんなことをしなくても普通にプログラミングできてしまうんですよね。
コードが増えてきたり、使いまわしたいコードが出てきたらわかりやすいように新しく標準モジュールを「挿入する」ということはありましたが、標準モジュールに新しく「名前をつける」ということはこれまでしたことはなかったです。
なぜなら、その方法でも特に困らずモジュール間のプロシージャを実行できていたからです。
実際に次のようにしてみると確認できます。ExcelのVBEを開いてから標準モジュールを2つ挿入してください。ちなみに自分が確認した環境はExcel2013になります。
その中で「Module1」の方には以下のコードを入力
Sub メッセージ呼び出し() メッセージ End Sub
「Module2」の方には以下のコードを入力してください。
Sub メッセージ() MsgBox "AAA" End Sub
以上のコードを入力した後、Module1モジュールの「メッセージ呼び出し」を実行してみてください。何の問題もなく「AAA」と書かれたメッセージボックスが表示されるかと思います。
つまり「Module2.メッセージ」という風にコードを入力しなくても実行できるということです。
こういったやり方でも問題なくプログラミングできてしまっていたことが「プロシージャではなく、まずオブジェクト(モジュール)にアクセスする」という考え方が生まれるのを妨げていた気がします。
まとめ
今回のクラスモジュールの理解からやっと「オブジェクト指向」の入口に立てたような気がします。今までずっとこのクラスモジュールの理解に悩んでいました。
これまでインターネットを使ってクラスモジュールについていろいろなサイトを見てみましたがどうしても理解できませんでした。ですから自分の中でクラスモジュールというのは、一部のめちゃくちゃ頭が良い人にしか理解できない難解なもの、というイメージが構築されていきました。
でも今回の件で自分の中のモヤモヤが氷解したというか、「あぁ、確かにこういったやり方ができれば便利だな」という風に「クラスモジュール」とか「オブジェクト指向」の便利さが理解できてきた気がします。
プログラミングの中のオブジェクト指向を勉強する段階で、多くの人が躓くという話をたくさん聞いてきました。おそらくその原因のひとつは、今回自分が書いたような考え方になかなか到達できなかったのではないでしょうか。もしくはもっと別の所で難しい所があったのかもしれません。
とにかくクラスモジュールやオブジェクト指向について少しずつ理解できてきた感じがします。
コメント