PUSHとLSet
ユーザ定義型の動的配列に、どんどん新しい要素(レコード)を追加していきます。いわゆるPUSH*1の作業だが、
For i=0 To 300 ReDim Preserved ary(0 To UBound(ary)+1) ary(UBound(ary)).member = ... Next i
とか、さすがにコレは
ReDim Preserved ary(0 To 300) For i=0 To 300 ary(UBound(ary)).member = ... Next i
として欲しいものである。ただ、単純な代入ならともかく、事前に処理内容から追加する要素数がわからないような場合に、
ReDim Preserved ary(0 To 500) ' 適当に大きく j=0 For i=0 To 300 if ... Then ary(j).member = ... : j = j + 1 Next i ReDim Preserved ary(0 To j)
とするよりも、
For i=0 To 300 If ... Then temp.member = ... Call Push(ary(), temp) End If Next i
というような書き方がやりたい場合もあります。
たぶん、Collection を使うのが VB の手本的なんだと思いますが、なにせゼロからコードを書いているわけではないので、動的配列で処理しなければ変更箇所が多すぎて参ってしまいます。
そこで、Push サブルーチンは、どのように実装すればいいのでしょうか?
Sub Push(ByRef Ary() as MyType, Item as MyType) ReDim Preserved Ary(LBound(Ary) To UBound(Ary)+1) Ary( UBound(Ary) ) = Item End Sub
これでいいんでしょうかね?このような関数は、まだ作っていませんが、たぶんこんな感じになるだろうと思いました。
さて、問題のユーザ定義型は
Public Type USerDefChild lngValue as Long strArray() as String End Type Public Type UserDefRec lngValue As Long strValue As String strArray() As String Children() As UserDefChild End Type
こんな感じでして、自信が文字列型の動的配列とか、別のユーザ定義型の動的配列を持っていて、さらにそのメンバーのユーザ定義型にも文字列の動的配列があったりします。
これって、=で代入しても平気なんでしょうか?
VB6のランゲージリファレンスによると、ユーザ定義型の変数を=で代入した場合、個別の基本型に対して代入文を記述したのと同じ効果があるとなっています。つまり、
ex1) Dim a As UserType Dim b As UserType a = b ex2) Dim a As UserType Dim b as UserType a.member1 = b.member1 a.member2 = b.member2 : 'すべてのメンバーを個別に代入する
という2つのコードは同じ結果になるようです。
ただし、例外として
- オブジェクト型
- 動的配列
- ユーザ定義型
のメンバーを含んだ型に対しては正常に働かないとなっています。ダメじゃん!!
これを解決するのか、既存のコードでは何度か代入時に LSet というプレフィックスキーワードが出てきます。
というわけで、LSet についても調べます。ヘルプを読んでも左辺値と右辺値の型が異なるときに複製の保障ができないとか注意が記載されているだけで、いったいどういう複製をしてくれるのかさっぱり記述がありません。
適当に検索したところ、この機能は基本的に LSet を付与しない代入と違い、基本型の要素はメモリイメージを複製することによって代入を処理し、Object や動的配列といった型に対してはこまめに代入処理を再帰してくれる模様で、うまい具居合いにディープコピーが作成できるようです。
・・・ようです、というのは、はっきりとした確信がもてるだけの情報にたどりつけなかったからです。とりあえず .NET では文字列型以外への LSet は廃止されたようですし、前述のような環境でデバッグシンボルがないPC上では、この LSet が VBAssignRec*2 という内部関数から OLEAUT32.DLL の中へ入ったあたりで調査を切り上げた。*3