VBAで外部のオブジェクトを扱うには、事前バインド方式と遅延バインド方式の2種類がある。事前バインドとはいわゆる参照設定のことで、遅延バインドとはCreateObject関数によるオブジェクト生成を指す。
さて、どちらが良いかと言われると一長一短なので困る。
メリット・デメリットは概ねこんなかんじ。
事前バインド(参照設定) | 遅延バインド(CreateObject) | |
メリット | 型がきっちり決まるのでコーディング中に入力補完の恩恵を受けられる。 | コードだけで完結するのでネットで公開するのが楽。 |
デメリット | コードだけで完結しないのでネットにコードを掲載する場合に説明が面倒くさい。ライブラリのバージョン違いなどで別のPCでは参照設定しなおさないと動作しないケースがある。 | 実行するまで型が決まらないのでコーディング中に入力補完の恩恵を受けられない。 |
事前バインディングのほうが若干スピードも速いんだけれど、微々たるものなので今回は無視する。
では具体的に例を挙げて説明する。
以下はどちらもFileSystemObjectを利用してCドライブ配下のフォルダ数を取得するプログラムだ。
Sub 事前バインド方式() '↓参照設定しているので固有型が使用できる。 Dim fso As FileSystemObject '↓参照設定しているので固有型を用いたNewによるオブジェクト生成ができる。 Set fso = New FileSystemObject '↓コーディング中にプロパティ・メソッドの入力候補が表示される。 Debug.Print fso.GetFolder("C:\").SubFolders.Count End Sub Sub 遅延バインド方式() '↓参照設定していないので固有型は使用できない。 Dim fso As Object '↓参照設定していないので固有型を用いたNewによるオブジェクト生成はできない。 Set fso = CreateObject("Scripting.FileSystemObject") '↓コーディング中にプロパティ・メソッドの入力候補が表示されない。 Debug.Print fso.GetFolder("C:\").SubFolders.Count End Sub
さて、プログラムを書くときは入力候補が表示されたほうが助かるので開発中は参照設定、配布するときはバージョンの違いなどを吸収してほしいのでCreateObjectを使いたいとする。
いちいちコードを書き直すのは面倒なので何か良い手はないか。
そこで今回ご紹介するのは、プリプロセッサディレクティブを用いて定数1つで事前バインド方式と遅延バインド方式を切り替える方法だ。
なにやらややこしい単語であるが、プリ(事前の)プロセッサ(プロセスの)ディレクティブ(指示)ってことで、もう少し簡単にいうと「このマクロを実行する前にあらかじめやっておいてね」という指示になる。
種類もそんなに無いのですぐ覚えられると思う。
ではサンプルコード。
#Const REF = True Sub 定数REFにより切り替え可能() #If REF Then Dim fso As FileSystemObject Set fso = New FileSystemObject #Else Dim fso As Object Set fso = CreateObject("Scripting.FileSystemObject") #End If Debug.Print fso.GetFolder("C:\").SubFolders.Count End Sub
この、ナンバーサイン(#)で始まっている部分がプリプロセッサディレクティブだ。
このマクロは実行の前にまず定数REFによってIf文が判定され、以下のように整形される。
※整形といっても内部的なものなので、実際にコードが書き換わるわけではない。
Sub 定数REFにより切り替え可能() Dim fso As FileSystemObject Set fso = New FileSystemObject Debug.Print fso.GetFolder("C:\").SubFolders.Count End Sub
もし#Const REF = Falseとしていたら、こうなる。
Sub 定数REFにより切り替え可能() Dim fso As Object Set fso = CreateObject("Scripting.FileSystemObject") Debug.Print fso.GetFolder("C:\").SubFolders.Count End Sub
それから実行されるのだ。
あるいはオブジェクトの生成部分はまとめてしまってもよい。
#Const REF = True Sub 定数REFにより切り替え可能() #If REF Then Dim fso As FileSystemObject #Else Dim fso As Object #End If Set fso = CreateObject("Scripting.FileSystemObject") Debug.Print fso.GetFolder("C:\").SubFolders.Count End Sub
整形するとこのようになる。
Sub 定数REFにより切り替え可能() Dim fso As FileSystemObject Set fso = CreateObject("Scripting.FileSystemObject") Debug.Print fso.GetFolder("C:\").SubFolders.Count End Sub
fsoは固有型で宣言しているが、オブジェクトの生成はCreateObjectでしている。
結局入力補完が効くかどうかは変数の型で決まるのでオブジェクトの生成自体はどちらでも良い。
こうすると参照設定にしておいて、配るときだけ定数REFをFalseにすれば良くなるのでメンテナンスはしやすい。
まぁ、このために行数が増えるのはすこし難点ではあるが。。