【実務・中級編】【配列の完全複製(ディープコピー)】参照渡しによるデータ破壊を防ぐ多次元配列・連想配列のクローン処理 – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク

VBScriptを掌握する:参照の呪縛を断ち切る「完全なるディープコピー」の極意

Windowsの奥深くに潜み、今なお現役で業務自動化を支え続けるVBScript。しかし、多くのエンジニアがこの言語の「甘い罠」に嵌まり、本番環境でデータ汚染という致命的なバグを引き起こしている。

その最たる原因が、「配列とオブジェクトの参照渡し(Shallow Copy)」だ。

「変数に代入しただけなのに、元のデータまで書き換わってしまった」――この絶望を経験したことがない者は、まだVBScriptの本質に触れていないと言ってもいい。今回は、複雑な多次元配列や連想配列(Scripting.Dictionary)を完全に独立したインスタンスとして複製する、実戦仕様のディープコピー実装について講義する。

1. なぜ「代入」はあなたを裏切るのか

VBScriptにおいて、スカラー変数(数値や文字列)の代入は「値のコピー」だ。しかし、配列やオブジェクト(Dictionaryなど)を `B = A` と記述した瞬間、それは実体のコピーではなく、メモリ上の同じ場所を指し示す「別名(エイリアス)」を作成したに過ぎない。

恐怖のシャローコピー例

Dim Source, Clone
Source = Array(“A”, “B”, “C”)
Clone = Source ‘ これが罠だ

Clone(0) = “Z”

‘ Source(0) も “Z” に書き換わっている!
WScript.Echo Source(0) ‘ 結果: Z

この挙動を理解せずに大規模なデータ加工ロジックを組むと、中間キャッシュ用の配列を操作したつもりが、マスターデータまで破壊するという惨劇を招く。プロフェッショナルなら、「コピーが必要なときは、明示的に新しい領域を確保し、中身を詰め直す」という設計思想を徹底せねばならない。

2. 実戦型:ディープコピー・エンジンの設計

単なる1次元配列なら `Array()` 関数やループで済むが、実務では「配列の中にDictionaryがある」「2次元配列の中にさらに配列がある」といった入れ子構造(ネスト)が頻出する。

これらに対応するためには、再帰的(Recursive)に型を判定し、それぞれの階層で新しいインスタンスを生成する汎用関数が必要だ。以下に、私が長年のプロジェクトで磨き上げた「究極のクローン関数」を提示する。

プロダクション・コード:`CloneData` 関数

‘ ==========================================================================
‘ 関数名: CloneData
‘ 概要 : 配列、多次元配列、Scripting.Dictionaryを再帰的に完全複製する
‘ 引数 : src – 複製元データ
‘ 戻り値: 複製された独立したデータインスタンス
‘ ==========================================================================
Function CloneData(ByVal src)
Dim dest, i, j, k, keys, key

Select Case VarType(src)
‘ — 配列の場合 (Array) —
Case vbArray, vbArray + vbVariant
Dim dimensions
dimensions = GetArrayDimensions(src)

If dimensions = 1 Then
‘ 1次元配列の複製
ReDim dest(UBound(src))
For i = LBound(src) To UBound(src)
‘ 再帰呼び出しにより、要素が配列やDictionaryでも対応
If IsObject(src(i)) Then
Set dest(i) = CloneData(src(i))
Else
dest(i) = CloneData(src(i))
End If
Next
ElseIf dimensions = 2 Then
‘ 2次元配列の複製 (業務で最も多用されるExcel形式)
ReDim dest(UBound(src, 1), UBound(src, 2))
For i = LBound(src, 1) To UBound(src, 1)
For j = LBound(src, 2) To UBound(src, 2)
If IsObject(src(i, j)) Then
Set dest(i, j) = CloneData(src(i, j))
Else
dest(i, j) = CloneData(src(i, j))
End If
Next
Next
Else
‘ 3次元以上の特殊ケースは必要に応じて拡張
Err.Raise 500, “CloneData”, “3次元以上の配列はサポート外です。”
End If
CloneData = dest

‘ — オブジェクトの場合 (Dictionaryなど) —
Case vbObject
If TypeName(src) = “Dictionary” Then
Set dest = CreateObject(“Scripting.Dictionary”)
‘ 比較モード(大文字小文字区別など)も継承
dest.CompareMode = src.CompareMode
keys = src.Keys
For Each key In keys
If IsObject(src(key)) Then
Set dest(key) = CloneData(src(key))
Else
dest(key) = CloneData(src(key))
End If
Next
Set CloneData = dest
Else
‘ その他のオブジェクト(FileSystemObject等)は参照渡し
Set CloneData = src
End If

‘ — 基本型 (String, Integer, Double, Boolean, Null, Empty) —
Case Else
CloneData = src
End Select
End Function

‘ 配列の次元数を取得するヘルパー関数
Function GetArrayDimensions(arr)
Dim dimCount, tmp
On Error Resume Next
dimCount = 0
Do While True
tmp = UBound(arr, dimCount + 1)
If Err.Number <> 0 Then Exit Do
dimCount = dimCount + 1
Loop
On Error GoTo 0
GetArrayDimensions = dimCount
End Function

3. この設計が「堅牢」である理由

① 再帰呼び出しによる「ネスト構造」の完全制覇

この関数の真骨頂は、`CloneData` 自体を内部で呼び出している点にある。
例えば、`Dictionary` の中に `Array` があり、その `Array` の中にさらに `Dictionary` があるような複雑なJSONライクなデータ構造であっても、この関数はメモリの末端まで辿り、すべての参照を切り離す。

② 多次元配列への対応

業務自動化(特にExcel操作やDB操作)においては、2次元配列がデータの最小単位となることが多い。このコードでは、次元数を判定するヘルパー関数を備え、実務で頻出する2次元配列までを確実にカバーしている。

③ DictionaryのCompareMode継承

意外と忘れがちなのが `Dictionary.CompareMode` だ。
既存のDictionaryが「大文字小文字を区別しない(TextCompare)」設定だった場合、単純に新規作成するとデフォルトの「BinaryCompare」に戻ってしまう。このコードではその設定もしっかりと引き継いでいる。

4. 現場での運用アドバイス:ファイル・DB連携の注意点

ディープコピーを駆使するような高度なスクリプトを書く際、以下の2点には細心の注意を払ってほしい。

1. メモリ消費のトレードオフ:
ディープコピーは安全だが、巨大な多次元配列(数万行のレコードなど)を複製すれば、当然メモリを倍消費する。WSHのプロセスは32bit環境の制約を受けることが多いため、不要になった複製元は `Erase`(配列)や `Set … = Nothing`(Dictionary)で明示的に解放せよ。

2. データベースからの `GetRows`:
ADO(ActiveX Data Objects)の `GetRows` で取得した配列は2次元だが、これをそのまま `CloneData` にかけるのは非常に有効だ。レコードセットを閉じた後でも、安全にデータを加工・再利用できる。

結論:コードの美しさは「副作用のなさ」に宿る

VBScriptのような古い言語を使いこなすコツは、言語の貧弱さを嘆くことではなく、その「癖」を逆手に取った堅牢なライブラリを自ら構築することにある。

今回紹介した `CloneData` をあなたの標準モジュールに組み込めば、参照渡しによる「不可解なバグ」は絶滅するはずだ。データの流れを完全にコントロール下に置き、副作用のない、保守性の高いツールを組み上げてほしい。

それが、この広大なWindowsエコシステムを掌握するチーフアーキテクトとしての第一歩である。

タイトルとURLをコピーしました