VBAのプログラムを勉強している方で、SubとFunctionは使いこなせているけど、この↓クラスモジュールって何?という方がいるかと思います。


クラスのアイコンってなんか不気味ね。
ここではクラスの細かい定義は置いといて、実践的なところでクラスを使うと何が良いかということをお伝えします。
例えば、Functionで、何か計算を行ったときに、その結果を返すことはできますが、エラーになった場合のエラー内容を一緒に戻すことはできませんね。
そういう時は、例えば「エラーの場合は0を返す」のようなマイルールで運用するような形になりがちですが、それがエラーなのか本当に0なのか分かりません。
また、Functionの引数がたくさんあるとき、例えば下記のような条件。
顧客番号1000番以上、関西地区、担当者Aさん、年間売り上げ100,000円以上。
このように条件が多いと、引数が非常に長くなりますよね。
'呼び出し元
Sub check()
Dim customerID As Integer
Dim address As String
Dim tanto As String
Dim uriage As Integer
Dim custCheckResult As Boolean
For RowCount = 2 To 100
'1列目 - 顧客番号
'2列目 - 住所
'3列目 - 担当者
'4列目 - 年間売上
customerID = Cells(RowCount, 1)
address = Cells(RowCount, 2)
tanto = Cells(RowCount, 3)
uriage = Cells(RowCount, 4)
'引数が多いとわかりづらい・・
custCheckResult = CustomerCheck(customerID, uriage, address, tanto)
'結果を5列目に記載
Cells(RowCount, 5) = custCheckResult
Next
End Sub
'引数が多いとわかりづらい・・
Function CustomerCheck(customerID, uriage, address, tanto)
On Error GoTo ErrorShori
If customerID >= 1000 And _
uriage >= 100000 And _
InStr("大阪", address) > 0 And _
tanto = "Aさん" Then
CustomerCheck = True
Else
CustomerCheck = False
End If
Exit Function
ErrorShori:
MsgBox ("エラーが発生しました。怒らずに落ち着いてから開発元へスクリーンショットを送ってください。" & vbCrLf & Err.Description)
End Function
このようにFunctionの引数が多いと何番目が何の項目かがわかりにくく、プログラム変更を行ったときに、Functionの引数の場所を間違えたりしてしまいがちです。
また、もう一つの問題として、Functionでエラーが発生した場合、その都度ポップアップのメッセージが出てしまいます。
このコードでは2行目から100行目をループしていますが、もしすべてエラーが出てしまった場合、100行目まで毎回OKボタンを押すか、タスクマネージャーで落とすしか方法がありません。

タスクマネージャーにはいつもお世話になっています。
クラスを使用した場合、引数(渡す値)がわかりやすいのと、結果をいくつでも受け取ることが出来るので、上記のチェック結果とエラーかどうか、エラー内容などを受け取ることが出来ます。
ではさっそくクラスを追加してみましょう。
まずは下記のように左側のツリーで右クリックして、挿入 – クラスモジュールを選択します。

次に、追加されたクラスのプロパティで、今回はオブジェクト名をCustomersにしておきましょう。

それでは次にクラスを記述していきます。クラスにはプロパティとメソッドというものがあります。ものすごく簡単に言えば、プロパティとはデータ(Functionで言うところの引数や返り値)、メソッドとは動作(SubやFunction)のことです。

説明が簡単すぎて逆にわからん。
まずはプロパティからですが、これがなかなか面倒なので、挫折しないように、こちらも簡単にご説明します。
プロパティにはクラス側で受け取りたい値(CusotomerIDや売上など)と、クラスから渡したい値(チェック結果や、エラー有無など)を設定します。
受け取りたい値は下記のように、
Private CustomerID_ As Integer と、
Property Let customerID(value As Integer)
CustomerID_ = value
End Property
を記述します。_(アンダーバー)を今回は付けていますが、何でも構いませんが、呼び出し元とは異なる名称にする必要があるので、アンダーバーを付けています。
とりあえず下記丸ごとコピペで構いません。
' ----------------- プロパティの設定 -------------------
'--- クラスが受け取りたい値(Functionで言うところの引数)
Private CustomerID_ As Integer
Private Uriage_ As Integer
Private Address_ As String
Private Tanto_ As String
'
Property Let customerID(value As Integer)
CustomerID_ = value
End Property
Property Let uriage(value As Integer)
Uriage_ = value
End Property
Property Let address(value As String)
Address_ = value
End Property
Property Let tanto(value As String)
Tanto_ = value
End Property
'---
'--- クラスから渡したい値(Functionで言うところの返り値)
Property Get custCheckResult() As Boolean
custCheckResult = custCheckResult_
End Property
Property Get noError() As Boolean
noError = noError_
End Property
Property Get errorDesc() As String
errorDesc = errorDesc_
End Property
'---
' --------------------------------------------------------------------

長すぎだろこれ。もうFunction一発でいいや。

それ言ったらこの記事の意味ないわよ。
次にメソッドです。上記の下に続けてコピペしてください。
中身を見てもらうとわかりますが、内容は一番最初に例に挙げたFunctionとほぼ同じです。ただ、顧客チェック結果以外に、エラーの有無やエラー内容もプロパティに代入しています。
' ----------------- メソッドの設定(クラス内のSubとかFunctionのこと) -------------------
'顧客チェック
Sub custCheck()
On Error GoTo ErrorShori
If CustomerID_ >= 1000 And _
Uriage_ >= 100000 And _
InStr("大阪", Address_) > 0 And _
Tanto_ = "Aさん" Then
custCheckResult_ = True
Else
custCheckResult_ = False
End If
'エラーなし
noError_ = True
errorDesc_ = ""
Exit Sub
ErrorShori:
'エラーあり
noError_ = False
errorDesc_ = Err.Description
End Sub
' --------------------------------------------------------------------
クラスの作成はここまでです。プロパティの箇所が面倒ですが、書き方さえ覚えてしまえば、面倒なだけで難しくはないと思います。

その面倒なのが問題なんだが。。

VBAだからね。しょうがないね。
次に呼び出し元です。
ここでは一先ずSheet1の方に下記をコピペしてください。
'呼び出し元
Sub check()
Dim result As Boolean
Dim cust As Customers
For RowCount = 2 To 100
'Customersクラスをセット
Set cust = New Customers
' --- クラスの処理を書く ---
'Customersクラスを解放
Set cust = Nothing
Next
End Sub
少しわかりにくいとは思いますが、Dim cust As Customers
で先ほど作成したクラスを宣言して、Set cust = New Customers
で、custという変数名でクラスをセットしています。(セットすることをインスタンス化といいます)
そして、Set cust = Nothing
でクラスを解放しています。変数の初期化みたいな感じです。
それではここで一つ試してほしいのが、上記の「クラスの処理を書く」のところで、 cust.(点)と打ってみてください。
VBE(VB Editor)の補完機能で、先ほど作成したクラスのプロパティとメソッドが一覧で表示されます。


自分が作ったのが自動で出てくるのは気持ちいい~

ちょっと何言ってるかわからない。
クラスが便利なのは、このように補完される点にもあります。
それでは残り分も含めたコードは下記になります。
'呼び出し元
Sub check()
Dim result As Boolean
Dim cust As Customers
For RowCount = 2 To 100
'Customersクラスをセット
Set cust = New Customers
'1列目 - 顧客番号
'2列目 - 住所
'3列目 - 担当者
'4列目 - 年間売上
cust.customerID = Cells(RowCount, 1)
cust.address = Cells(RowCount, 2)
cust.tanto = Cells(RowCount, 3)
cust.uriage = Cells(RowCount, 4)
cust.custCheck
If cust.noError = False Then
MsgBox ("エラーが発生しました。怒らずに落ち着いてから開発元へスクリーンショットを送ってください。" & _
vbCrLf & cust.errorDesc)
Exit Sub
End If
'結果を5列目に記載
Cells(RowCount, 5) = cust.custCheckResult
'Customersクラスを解放
Set cust = Nothing
Next
End Sub
まずは cust.customerID = Cells(RowCount, 1)
のところで、customerIDのプロパティに値を入れています。ここは順番は関係ありませんので、例えばcust.address = Cells(RowCount, 2)
を先に記述しても問題ありません。
ここがFunctionと比較すると、わかりやすいところです。Functionの場合、引数の順番で、渡す値が決まっていますが、このようになっていると、何の値を渡しているのかが明確ですね。
次に、cust.custCheck
のところで、顧客のチェックを行って、結果がcust.custCheckResultに入ってきます。
ここで便利なのが、cust.noErrorというプロパティには、チェックしたときにエラーがなかったかどうかが入っていますので、こちらがfalseの場合、メッセージを出して、Exit Subで処理を終了させることができます。
このように、コードは長くなってしまいますが、Functionだけでは出来ないことが可能になります。今回は一例を挙げて、クラスの良い点をご説明しましたが、実際にはまだまだほかにも利点がありますので、また今後ご紹介したいと思います。
もしまだクラスを使ったことが無いという方は、ぜひ一度試してみてください。

実際にステップ実行して1つずつの処理を確認してみるといいかもね。

なるほど。つまり細かく説明するのが面倒ってことね。
コメント