発端
MSDNを見ていると「FileSystemObject のプログラミング」のページに以下のような記述がありました。
FileSystemObject のインスタンスは、ほかのインスタンスを何回作成しても 1 つしか作成されません。
この記述の内容を確認するために、簡単に検証を行ってみました。
検証
1. 同じインスタンスなのか?
おもむろに以下のコードを実行してみましたが、Falseになりました。
Debug.Print CreateObject("Scripting.FileSystemObject") Is CreateObject("Scripting.FileSystemObject")
' -> False
少なくともGetObjectのように同じインスタンスを返すわけでは無さそうです。
2. インスタンスにかかる時間は?
以下のコードでインスタンス(CreateObject)にかかる時間を簡単に計測してみました。
普通にインスタンスをして即破棄1する場合と、1個インスタンスを確保してからインスタンスをする場合の2パターンの確認です。
使ったコード
Sub Test_CreateObjectTime()
Const CREATE_COUNT& = 1000&
Const PROG_ID$ = "Scripting.FileSystemObject"
Dim i&, startTime!, timeSpanSec!
'毎回新規インスタンス
startTime = Timer
For i = 1 To CREATE_COUNT
Call CreateObject(PROG_ID)
Next i
timeSpanSec = Timer - startTime
Debug.Print "キャッシュなし", timeSpanSec; "sec"
'1個インスタンスを保持しつつ新規インスタンス
startTime = Timer
Dim cache As Object
Set cache = CreateObject(PROG_ID)
For i = 1 To CREATE_COUNT
Call CreateObject(PROG_ID)
Next i
Set cache = Nothing
timeSpanSec = Timer - startTime
Debug.Print "キャッシュあり", timeSpanSec; "sec"
End Sub
結果
私のPCで実行した場合、以下のような結果となりました。
キャッシュなし 0.9960938 sec
キャッシュあり 0.0078125 sec
キャッシュあり(インスタンスを保持した方)の方が明らかに、処理時間が短くなっていることがわかります。
この挙動を有効利用するには?
処理中に何度もScripting.FileSystemObject
を使用する場合は、毎回インスタンスを破棄するのでは無く、どこかでインスタンスを保持しておいた方が処理時間的には良いということになります(その分メモリを使用しますが)。
Microsoft Scripting Runtime
を参照している場合
Public FSO As New Scripting.FileSystemObject
New 付き型宣言でグローバル変数を宣言しておき、宣言した変数経由で使用することで VBA側にインスタンスと参照の保持を任せることができます。
参照設定なしの場合
以下のように静的変数を使うことでキャッシュのようなことができます。
Property Get FSO() As Object 'Scripting.FileSystemObject
Static fso_ As Object
If fso_ Is Nothing Then Set fso_ = CreateObject("Scripting.FileSystemObject")
Set FSO = fso_
End Property
Static宣言の是非もあるので、一つの考え方としてどうぞ。
WSH
WSF形式を使用すると、object タグでグローバルなオブジェクト定数を宣言できます。
<?XML version="1.0" standalone="yes" ?>
<package>
<job id="fsoTestJob">
<!-- グローバルで宣言・インスタンスされるオブジェクト -->
<object id="FSO" progid="Scripting.FileSystemObject" reference="yes" />
<script language="VBScript"><![CDATA[
Option Explicit
'後は普通のVBScript
MsgBox TypeName(FSO)
]]></script>
</job>
</package>
通常のVBScript(.vbs)だと、グローバル変数は外部から書き換えの可能性があるため少々面倒になりそうです。
-
VBAでは参照カウント方式でオブジェクトの寿命を管理しているはずなので、変数に入れなければ破棄される。 ↩