Object型変数を積極的に使う
Object型変数を積極的に使う
今まで、Object
型の変数を積極的に使うことはなかった。
VBAで作成したツール類も、内輪で使うものばかりなので、CreateObject
を自分から使うことはまずなかった。参照設定してNew
ばっかりだった。
今回、Object
型変数を使ったら便利だな、と思った場面があったので紹介する。
Sheet1オブジェクトにPropertyとメソッドを設定する
シートモジュールにProperty
を設定してみる。
今回は、シートの基準位置を示すセルを、シートオブジェクトのProperty
にする。
リスト1 Sheet1モジュール
Public Property Get BaseCell() As Range Set BaseCell = Me.Range("A1") End Property
たったこれだけの単純なコード。
A1セルを、Sheet1
オブジェクトの基準セルを表すBaseCell
プロパティに設定している。
次に、Sheet1
オブジェクトにメソッドを設定する。
リスト2 Sheet1モジュール
Public Sub showBaseCellValue() Call MsgBox(Me.BaseCell.Value) End Sub
ご覧のとおり、MsgBox
を使って、先ほど設定したBaseCell
プロパティを参照して得られるRange
オブジェクト(すなわちSheet1
オブジェクトのA1セル)の値を表示するだけのメソッド。
使ってみる
Sheet1
を、
このようにしておいて、
次のコードで早速使ってみる。
リスト3 標準モジュール[ModuleMain]
Public Sub showBaseCellValueMain() Call Sheet1.showBaseCellValue End Sub
Sheet1
オブジェクトのshowBaseCellValue
メソッドを呼び出す。
当然、
こうなる。
Sheet1
オブジェクトのshowBaseCellValue
メソッド内部でBaseCellValue
プロパティを参照し、A1セルが返るので、A1セルのValue
プロパティの値(すなわち「ち~んw」)がメッセージボックスに表示されたわけだ。
Sheet2オブジェクトにもPropertyとメソッドを設定する
同じように、Sheet2
オブジェクトにも似たようなProperty
とメソッドを設定してみる。
ただし、今度は、
このように、基準セルをB2にする。
リスト4 Sheet2モジュール
Public Property Get BaseCell() As Range Set BaseCell = Me.Range("B2") End Property Public Sub showBaseCellValue() Call MsgBox(Me.BaseCell.Value) End Sub
もはや多言は要すまい。BaseCell
プロパティが指し示すセルの位置がB2に変わっただけだ。
使ってみる
今度は、上記のリスト3を次のように変更する。
リスト5 標準モジュール[ModuleMain]
Public Sub showBaseCellValueMain() ' Sheet1.showBaseCellValue' Sheet2.showBaseCellValue End Sub
Sheet1
オブジェクトに対する操作の部分をコメントアウトして、Sheet2
オブジェクトのshowBaseCellValue
メソッドを呼ぶコードにした。
実行すると、
当然こうなる。何の不思議もない。
問題
「似たようなシートがたくさんあるのだけれど、行き当たりばったりで行や列を追加してしまったせいで、基準位置となるセルがばらばら
」みたいなときには、このようにSheetX
オブジェクトのProperty
にしてしまう、というのは便利なテクニックだと思う。
ただし、このやり方だと早晩行き詰まることになる。
シートが増えるごとに、「似ているけれど少しづつ異なるコードが量産されてしまう」という問題である。
もちろん、量産する段階では、単純に「コピペして微修正」を繰り返すだけなので、たいした手間ではない。しかし、たとえば今回のshowBaseCellValue
に機能を追加しようとしたらどういうことになるか。
もちろん、量産した分だけ手間がかかるのである!
ふつう、こういうときは、「共通部分の括り出し」ということをする。
メソッドの括り出しを試みる
とりあえず、標準モジュールに次のようなコードを書いて括り出してみる。
リスト6 標準モジュール[SheetOperator]
最近、標準モジュールに名前を付けて、メソッドの機能ごとに分ける、ということをしています。今回は、SheetX
オブジェクトを操作するメソッドなので、モジュール名を「SheetOperator
」にしました。命名方法については、現在試行錯誤中です。参考になるコーディング規約等ございましたら、ご教示ください。
Public Sub showBaseCellValue(ByVal targetSheet As Worksheet) Call targetSheet.showBaseCellValue End Sub
引数でWorksheet
オブジェクトを渡して、それぞれのshowBaseCellValue
メソッドを実行する、という目論見。
しかしながら、この計画には、実はコード入力時点で既に暗雲が立ちこめている。
targetSheet.
の段階で、入力候補にshowBaseCellValue
が出てこないのである。
そして、その嫌な予感は、このメソッドを使ってみるとしっかり当たる。
使ってみる
リスト7 標準モジュール[ModuleMain]
Public Sub showBaseCellValueMain() ' Sheet1.showBaseCellValue' ' Sheet2.showBaseCellValue' Call SheetOperator.showBaseCellValue(Sheet1) End Sub
Sheet1
を引数として渡して、SheetOperator
モジュールのshowBaseCellValue
メソッドを実行する。
すると、
あえなくコンパイル・エラー!
これは、少し考えれば当たり前の話で、
showBaseCellValue
メソッド(BaseCell
プロパティも)は、あくまでもSheet1
オブジェクト、Sheet2
オブジェクトのメソッド(プロパティ)なのであって、Worksheet
オブジェクトのメソッド(プロパティ)ではない
ということなのである。
単純な理屈だけれど、案外見落としやすいポイントだと思う。
そこで、Object型の登場
要するに、SheetOperator
モジュールのshowBaseCellValue
メソッドにSheet1
オブジェクト、Sheet2
オブジェクトが渡ればいいのである。
よって、リスト6を次のように修正する。
リスト8 標準モジュール[SheetOperator]
Public Sub showBaseCellValue(ByVal targetSheet As Object) Call targetSheet.showBaseCellValue End Sub
Object
型の変数に格納して渡すことで、行った先でもSheet1
オブジェクト、Sheet2
オブジェクトとして振る舞ってくれるはずだ。
使ってみる
次のコードで実験。
リスト9 標準モジュール[ModuleMain]
Public Sub showBaseCellValueMain() ' Sheet1.showBaseCellValue' ' Sheet2.showBaseCellValue' Call SheetOperator.showBaseCellValue(Sheet1) Call SheetOperator.showBaseCellValue(Sheet2) End Sub
今度は、
うまくいった。
おわりに
Object
型の変数を使うことによって、コードを一箇所に集約することができた。
もちろん、Object
型の変数(引数)を使うと、コーディング中に入力候補が表示されなくて不便だが、その場合は、コーディング時には今回の場合だったら、ひとまずSheet1
で書いておいて、書き終わって実行テストが終わってから一気にtargetSheet
に置換する、というやり方をすればいいと思う。