VBAでのクラスの基本的な使い方
クラスを使うと効率的なプログラムを書く事が出来ます。
クラスはオブジェクト指向を実現するための機構で、ある機能に対する変数や関数をグループ化して使う事が出来ます。
こうする事で、後から変更したり、コードが見やすくなったりします。
また、プログラマーもクラスを設計する際に問題を整理することとなり、どのようにアプローチすれば良いか考えがまとまりやすくなります。
実例でクラスを学ぶ
色々と説明するよりも、まず基本的なクラスを作り、使ってみた方がイメージが分かり易いと思います。
以前、クラスについての記事を書きましたが、今回は実例を挙げてから解説します。
詳細はそちらのページを参照してください。
VBAで「オブジェクト」という言葉をよく耳にすると思います。
オブジェクトとはVBAではセル、シート、ブックなどの部品を指します。
これらの実態はクラスなので、オブジェクト=クラスと考えても良いと思います。
普段我々は、VBAの出来上がったクラスを使っていると言えます。
クラスとは、変数と関数をグループ化して特別な変数のように扱えるものと捉えて良いと思います。
これは私の印象なのですが、クラスは様々な設定値(プロパティ値)を保持しつつ、それらを使い回すことができます。
クラスの作り方
今回は「車」をクラスと捉えて簡単なクラスを作り、使ってみたいと思います。
と言っても複雑なものではありません。
まず、クラスは標準モジュールではなく、クラスモジュールに書きます。
プロジェクトウインドウから右クリックから「挿入」とすると「クラスモジュール」がありますのでこれをクリックしてください。
出来上がったクラスは「Class1」というクラス名になっていますので、適当な名前に変更します。
今回は「CCar」とします。最初の C はクラスの C を示しています。

最初に実例を示します。
'/// CCar クラス (クラスモジュールに書きます。)///
'メンバ変数
Public mOwner As String
Private mColorNum As Long
Private mColor As String
Private mRange As Range
'初期化
Private Sub Class_Initialize()
mTypeName = ""
mOwner = "no Owner"
mColorNum = 0
mColor = ""
Set mRange = Nothing
End Sub
Public Property Let Color(n As Long)
mColorNum = n
'他のメンバ変数に値を代入できる
If n = 3 Then
mColor = "Red"
ElseIf n = 33 Then
mColor = "Blue"
Else
mColor = "---"
End If
End Property
Public Property Get Color() As Long
Color = mColorNum
End Property
Public Property Set MyRange(r As Range)
Set mRange = r
End Property
Public Property Get MyRange() As Range
Set MyRange = mRange
End Property
'メソッド
Public Sub startPos()
mRange.Value = mOwner
mRange.Interior.ColorIndex = mColorNum
End Sub
Public Sub Forward(n As Long)
If n <= 0 Then Exit Sub
If n > 1 Then
Dim i As Long
For i = 1 To n - 1
mRange.Offset(0, i).Value = "- - -"
Next i
End If
mRange.Interior.ColorIndex = 0 ' 背景色 塗りつぶしなし
mRange.Value = "● - -"
Set mRange = mRange.Offset(0, n)
mRange.Interior.ColorIndex = mColorNum
mRange.Value = mOwner
End Sub
クラスは、メンバ変数、プロパティ、メソッドという3つに分けて書いていくと見やすいと思います。
メンバ変数
クラス内で使う変数を「メンバ変数」と呼びます。それぞれ属性を付けられるのですが、Privateにして外から勝手に変更できないようにするのが普通です。
今回は、「外から勝手に変更できる」のを体験するためにPublic属性のメンバ変数を1つ作りました。
プロパティ
プロパティとは、外からメンバ変数にアクセスするための仕組みです。
普段書きなれないと、回りくどいように感じるかもしれませんが、こうする事でクラスと外部とのやり取りを制限する事ができ強固なプログラムがかけるようになります。
入力にはLet。出力はGetを使います。両方とも同じ名前のプロパティとすれば、変数のように入力や出力が可能です。
また、Letの時に別のメンバ変数への代入を行うなど高度な使い方もできます。
注意が必要なのはVBAのオブジェクトを使うときです。
この時は、Letの代わりにSetを使います。また、内部の代入の際にもSetが必要です。
外部で代入するときもこれまでと同じようにSetが必要になります。
メソッド
メソッドには属性(Private、Public)を付ける事が出来ます。
こうする事で、クラス内部のみで使いたいメソッドとクラスの外部から呼び出したいメソッドを色分けできます。
クラスの使い方
これまでの説明はクラスの設計方法でした。
実際にクラスを使うには、設計図から実体化する必要があります。
また、実体化したクラスに名前を付けて扱えるようにクラス型の変数を用意する必要があります。
例を示します。
'/// このコードは、標準モジュールに書きます。 ////
Sub TestClass()
Dim myCar As CCar
Set myCar = New CCar
'シートをクリア(以前の記録を消す)
Range("A1:AA20").Interior.ColorIndex = 0
Range("A1:AA20").ClearContents
'プロパティ
myCar.mOwner = "Tom"
myCar.Color = 33
Set myCar.MyRange = Range("C3")
'メソッド
myCar.startPos
myCar.Forward 5
End Sub
クラス型の変数を宣言したら、Set 変数名 = New クラス名 とすることで実体化します。
実体化することを「インスタンス化」と言います。
これは、設計図からPCのメモリ上に展開させる事になります。
こすることで、クラスが使用できるようになしました。
クラス内部の変数(メンバ変数)にアクセスするにはプロパティ値に設定すればOKです。
この書き方はVBAのオブジェクトと同じ書き方ですね。
メソッドも同じように クラスインスタンス.メソッド という書き方で実行できます。
クラスを使わない書き方
これまでのクラスを使ったコードをクラスを使わない書き方にするとどうなるでしょうか?
2つを比べて利点や欠点を見てみましょう。
'/// このコードは、標準モジュールに書きます。///
Sub TestCode()
'シートをクリア(以前の記録を消す)
Range("A1:AA20").Interior.ColorIndex = 0
Range("A1:AA20").ClearContents
'変数前言
Dim Owner As String
Dim nColor As Long
Dim aRange As Range
'変数初期化
Owner = "Tom"
nColor = 33
Set aRange = Range("C3")
'メソッド
aRange.Value = Owner
aRange.Interior.ColorIndex = nColor
Stop
Call goForward(5, aRange, nColor, Owner)
End Sub
Public Sub goForward(n As Long, rng As Range, myColor As Long, own As String)
If n <= 0 Then Exit Sub
If n > 1 Then
Dim i As Long
For i = 1 To n - 1
rng.Offset(0, i).Value = "- - -"
Next i
End If
rng.Interior.ColorIndex = 0 ' 背景色 塗りつぶしなし
rng.Value = "● - -"
Set rng = rng.Offset(0, n)
rng.Interior.ColorIndex = myColor
rng.Value = own
End Sub
小さなコードなので、クラスの恩恵は無いように思えます。
クラスを使わない場合、プロパティなどのコードは不要ですので短く書けています。
ですので、小さな規模ではクラスにする必要性は無いようです。
クラスの利点としては、変数の宣言がクラスのみでOKだという事です。
今回のコードでは、メンバ変数にあたる変数を全て関数に押し込んでいます。
これを関数の外に出してグローバル変数として使う場合は、管理が厄介になる事が予想されます。
また、最後の関数(goForward)は外に出して呼び出していますが、変数を全て引数として渡さなければならず煩雑になってしまいます。クラスを用いた例では、 myCar.Forward 5 としたので非常に分かり易い書き方が出来ています。
今回の例ではあまり感じませんが、クラスを使った書き方ですと、For文やIf文などが少なく、整然としたコードになるケースが多いです。
そのため、プログラムの順序がイメージしやすく、大きな流れはクラスの外で、小さな流れはクラス内部で書くような形になってきます。
まとめ
クラスを使った書き方というのは、最初から最後まで一様に流れていくような処理には不向きかもしれません。
具体的には、転記を伴う集計処理などです。
反対に変数にしなければならない要素が多く、それらを臨機応変に組み合わせていくような処理にはクラスが向いています。
具体的にはゲームプログラムなどです。
クラスを使ったオブジェクト指向とは、想定される処理をあらかじめ用意しておいて、それを組み合わせていくというようなやり方になります。そのため、後から仕様が変更になったり、機能を追加、修正する事になっても変更が楽で最初から作り直すというような事にはならずに済みます。
また、クラス内の変数(メンバ変数)を外部から隔離して隠蔽できることからバグを防ぐ書き方ができるのもクラスの利点と言えます。