LoginSignup
2
3

More than 5 years have passed since last update.

FileSystemObject多用時はキャッシュすると良い

Posted at

発端

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 タグでグローバルなオブジェクト定数を宣言できます。

fsotest.wsf
<?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)だと、グローバル変数は外部から書き換えの可能性があるため少々面倒になりそうです。


  1. VBAでは参照カウント方式でオブジェクトの寿命を管理しているはずなので、変数に入れなければ破棄される。 

2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3