銀河鉄道

【VBA】FSO(“Scripting.FileSystemObject”)|シングルトン化して使う

サムネイル
[VBA]fsoSingleton pattern

fsoをセットする

CreateObject(“Scripting.Dictionary”)を使う

Dim fso as Object
Set SetFSO = CreateObject("Scripting.FileSystemObject")

シングルトン化|Singleton pattern

モジュール変数に1個だけ作って、ずっとそれを使い回すこと

通常のやり方(非シングルトン)

Dim fso As Object
Set fso = CreateObject("Scripting.FileSystemObject")

これを毎回呼ぶたびに書くと、

  • COMオブジェクトが何回も生成される(重い)
  • メモリも少しずつ増える
  • 特に大量ループで呼ぶとパフォーマンス低下

シングルトン化のやり方

' モジュール変数として保持
Private mFSO As Object

' 初回だけ生成、それ以降は同じインスタンスを返す
Private Function FSO() As Object
    If mFSO Is Nothing Then
        Set mFSO = CreateObject("Scripting.FileSystemObject")
    End If
    Set FSO = mFSO
End Function

' 解放
Public Sub ResetFSO()
    Set pFSO = Nothing
End Sub

' ▽ 以下、必要なFSO系の関数を置く

' Return extension without a leading dot, "" if absent (FSO-conformant).
' 先頭ドットなしで拡張子を返す。なければ ""(FSOの仕様に合わせる)。
Public Function GetExtensionFSO(ByVal filePath As String) As String
	GetExtensionFSO = FSO().GetExtensionName(filePath)	' e.g., "xlsx" or ""
End Function

' Return base name (filename without path), including extension.
' ベース名(パスを除いたファイル名、拡張子込み)を返す。
Public Function GetBaseNameFSO(ByVal filePath As String) As String
	GetBaseNameFSO = FSO().GetFileName(filePath)
End Function

' Return parent folder path, "" if none.
' 親フォルダのパス。無ければ ""。
Public Function GetParentPathFSO(ByVal filePath As String) As String
	On Error Resume Next
	GetParentPathFSO = FSO().GetParentFolderName(filePath)
	On Error GoTo 0
End Function

' Combine path segments robustly.
' パス結合(区切りの重複/欠落に強い)。
Public Function JoinPathFSO(ByVal parentPath As String, ByVal childName As String) As String
	JoinPathFSO = FSO().BuildPath(parentPath, childName)
End Function

' Ensure directory exists (create if missing).
' ディレクトリが無ければ作成。
Public Sub EnsureFolderFSO(ByVal folderPath As String)
	If Not FSO().FolderExists(folderPath) Then
		FSO().CreateFolder folderPath
	End If
End Sub

こうしておくと:

  • 初回だけ CreateObject が実行される
  • 2回目以降は既に生成済みのオブジェクトを返す
  • COMの生成コストを一度きりにできる

使い方例|Usage

' Typical usage — no need to create/cleanup FSO each time.
' 典型例 — 呼ぶたびにFSO生成/破棄は不要。
Sub Sample()
	Dim ext As String
	ext = GetExtensionFSO("C:\data\sales.xlsx")	' → "xlsx"

	Dim base As String
	base = GetBaseNameFSO("C:\data\sales.xlsx")	' → "sales.xlsx"

	Dim parent As String
	parent = GetParentPathFSO("C:\data\sales.xlsx") ' → "C:\data"

	Call EnsureFolderFSO(JoinPathFSO(parent, "backup"))
End Sub

これで、100回呼んでも内部では同じFSOインスタンスが使われ続ける。

どんな時に使う?

状況シングルトン化すべき?理由
FSOをループ内で何回も使うすべきCOM生成を最小化
一回しか使わない不要オーバーエンジニアリング
共通ユーティリティとして複数関数から使う有効メモリ効率・速度向上

解放せずに保持してもいい?

FSOのモジュール内シングルトン保持はOK

  • FSOは軽量で、外部資源を保持しない(ファイルを開いてるわけではない)
  • 役割はパス操作、存在確認、ファイル/フォルダ作成の窓口
  • 使い回しても副作用は小さい
  • 逆に、都度CreateObjectを繰り返すのはオーバーヘッドになりやすい

頻繁に呼ぶなら保持、たまにしか使わないなら都度生成でも可

参考|すぐ解放すべきもの

危険なのは“外部資源”

  • 例:TextStream(ファイルストリーム)、ADODB.ConnectionRecordsetExcel.Workbook開放忘れは危険。
  • これらは**使い終わったら即Close + Set ... = Nothing**が基本。

Referenced Insights & Citations

| Singleton Pattern|シングルトンパターン |
| Module Variable|モジュール変数 |
| Object Reuse|オブジェクトの再利用 |
| Singleton Reuse|シングルトン再利用 |
| COM Object|COMオブジェクト |
| Memory Efficiency|メモリ効率 |
| Initialization Cost|初期化コスト |
| Reference Counting|参照カウント |
| External Resource|外部資源 |
| TextStream|テキストストリーム |
| Lifecycle Hook|ライフサイクルフック |
| Overhead|オーバーヘッド |
| Scope Release|スコープ解放 |
| Resource Leak|リソースリーク |

One instance,
reused efficiently.
That’s the heart of Singleton.

著者

author
月うさぎ

編集後記:
この記事の内容がベストではないかもしれません。

記事一覧