配列変数を値渡しにする

値渡しの配列引数

配列引数を値渡しにする方法

akashi-keirin.hatenablog.com

このときにも書いたとおり、通常、プロシージャの引数に配列を渡すとき、

Public Sub hogehoge(ByVal foo() As String)

みたいにすると、

f:id:akashi_keirin:20180401225819j:plain

こんなエラーが出る。

ところが、VBA四天王(だから、内わけはどうなっとるんだよw)の一人、id:imihito さん曰く、

引数の型をVariant型にすると、配列も値渡しで受け取ることができたりします

とのこと。

さらに、

オブジェクト型配列だと型情報が抜ける

とも。

へえ。いっぺんやってみよう。

Variant型引数にオブジェクト型の配列を渡してみる

メンドクサイので、2ついっぺんにやってみる。

リスト1 標準モジュール
Public Sub testDeclareObjectArgByVal()
  Dim Sh As Worksheet
  Set Sh = ThisWorkbook.Worksheets("Sheet1")
  Dim vtlTable(1) As New VirtualTable
  vtlTable(0).init Sh.Range("A1").CurrentRegion    '……(1)'
  vtlTable(1).init Sh.Range("J1").CurrentRegion
  Call returnValue(vtlTable)    '……(2)'
End Sub

Private Sub returnValue(ByVal targetVirtualTable As Variant)    '……(3)'
  Dim i As Integer
  For i = LBound(targetVirtualTable) To UBound(targetVirtualTable)    '……(4)'
    Debug.Print targetVirtualTable(i).valueOfCell(2, 2)
  Next
  Debug.Print TypeName(targetVirtualTable)    '……(5)'
End Sub

せっかくなので、

akashi-keirin.hatenablog.com

前回リニューアルしたVirtualTableクラス型の配列を使ってやってみた。

VirtualTableクラスのコードについてはコチラをどうぞ。

f:id:akashi_keirin:20180401225826j:plain

こんな表を用意して、

(1)からの2行

vtlTable(0).init Sh.Range("A1").CurrentRegion
vtlTable(1).init Sh.Range("J1").CurrentRegion

で、initメソッドを用いて、A1セルのCurrentRegionをvtlTable(0)に、J1セルのCurrentRegionをvtlTable(1)に渡してそれぞれ初期化する。

(2)の

Call returnValue(vtlTable)

では、引数にVirtualTableクラス型の配列変数vtlTableを渡してreturnValueプロシージャを呼び出している。

returnValueプロシージャは、(3)からの7行

Private Sub returnValue(ByVal targetVirtualTable As Variant)    '……(3)'
  Dim i As Integer
  For i = LBound(targetVirtualTable) To UBound(targetVirtualTable)    '……(4)'
    Debug.Print targetVirtualTable(i).valueOfCell(2, 2)
  Next
  Debug.Print TypeName(targetVirtualTable)    '……(5)'
End Sub

(3)の

Private Sub returnValue(ByVal targetVirtualTable As Variant)

で、引数の型をVariant型にして、ByValキーワードを付けている。

見た目上、値渡しになっているが、その反面、見た目上は引数targetVirtualTableが配列変数だとは全く分からない。

このプロシージャ内では、まず(4)からの3行

For i = LBound(targetVirtualTable) To UBound(targetVirtualTable)
  Debug.Print targetVirtualTable(i).valueOfCell(2, 2)
Next

で、VirtualTableクラスのインスタンスそれぞれが内部で保持している2次元配列(元は表)の2行2列目の値をイミディエイトに書き出し、それが終わったら、(5)の

Debug.Print TypeName(targetVirtualTable)

で、TypeName関数を用いて、引数として受け取ったtargetVirtualTableの型名をイミディエイトに出力する、という至って投げやりな処理を行う。

実行結果

f:id:akashi_keirin:20180401225834j:plain

おお! 3行目が「Object()」になっとる!

たしかに、もともとVirtualTable型であったという情報は失われているっぽい。

おわりに

この他にも、呼び出され側のプロシージャ(今回の場合はreturnValueプロシージャ)をコーディングするとき、targetVirtualTableの型が未確定なため、Intellisenseが全く効かず、非常に書きにくかった、ということも申し添えます。

わざわざ値渡しにする意義が今ひとつ見いだせなかった。

@akashi_keirin on Twitter