【问题标题】:Pass array to ParamArray将数组传递给 ParamArray
【发布时间】:2014-01-14 00:16:15
【问题描述】:

是否可以将数组的所有元素传递给 ParamArray?

例如,我想将一个 ParamArray 传递给另一个 ParamArray:

Sub test()
    p1 "test", "banane", "birne"
End Sub

Sub p1(ParamArray keys() As Variant)
    p2 keys 'should be the same as: p2 "test", "banane", "birne"
End Sub

Sub p2(ParamArray keys() As Variant)
    Dim key As Variant
    For Each key In keys
        Debug.Print key 'Run-time error '13' Type mismatch (key is an array)
    Next key
End Sub

在这种情况下,p2 的 ParamArray 不包含 keys 的元素,但它获得了数组对象 keys。因此,我必须检查是否传递了数组:

Sub test()
    p1 "test", "banane", "birne"
    p2 "test", "banane", "birne"
End Sub

Sub p1(ParamArray keys() As Variant)
    p2 keys
End Sub

Sub p2(ParamArray params() As Variant)
    Dim keys As Variant
    If IsArray(params(0)) Then
        keys = params(0)
    Else
        keys = params
    End If

    Dim key As Variant
    For Each key In keys
        Debug.Print key
    Next key
End Sub

但与 Java 相比,这有点尴尬:

public class VarArgs {

    public static void main(String[] args) {
        p1("test", "banane", "birne");
        p2("test", "banane", "birne");

        String[] array = {"test", "banane", "birne"};
        p1(array);
        p2(array);
    }

    public static void p1(String... strings) {
        p2(strings);
    }

    public static void p2(String... strings) {
        for (String string : strings) {
            System.out.println(string);
        }
    }

}

在 Java 中我不必区分。但这在 VBA 中可能是不可能的。

感谢您的帮助,
迈克尔

【问题讨论】:

  • This link 建议在必要时解包 ParamArray,但它基本上是您方法的通用版本。就个人而言,如果可能的话,我会远离 ParamArrays,更喜欢 VBA Collection 类或自定义类。
  • 它看起来像 IS possible in VB.Net,因为您可以为 ParamArray 定义特定类型(如 Java 示例中的 String),然后您可以传递“一个数组具有与参数数组的元素类型相同的元素类型。"

标签: vba parameters parameter-passing paramarray


【解决方案1】:
Sub test()
    p1 "test", "banane", "birne"
End Sub

Sub p1(ParamArray keys() As Variant)
    p2 keys
End Sub

Sub p2(ParamArray keys() As Variant)
    Dim key As Variant
    For Each key In keys
        Debug.Print key(0) '<- Give an Index here.
    Next key
End Sub

【讨论】:

  • 感谢您的建议,但p2 "test", "banane", "birne" 会导致错误。 p2 keysp2 "test", "banane", "birne" 不能一视同仁。我必须检查是否通过了数组(请参阅编辑)。但与其他一些编程语言(例如 Java)相比,这很尴尬。
【解决方案2】:

paramArrays 很奇怪,但你可以使用普通的 Array,效果很好

 Sub test()
    Dim a As Variant: a = Array("test", "banane", "birne")
    p1 a
End Sub

Sub p1(keys As Variant)
    p2 keys
End Sub

Sub p2(keys As Variant)
    Dim key As Variant
    For Each key In keys
        Debug.Print key
    Next key
End Sub

【讨论】:

    【解决方案3】:

    试试:

    Sub p2(ParamArray keys() As Variant) 
    dim myKey as Variant  
     If IsArray(keys(0)) Then
            myKey = keys(0)
        Else
            myKey = keys()
     End If
    
    ...
    end sub
    

    【讨论】:

      【解决方案4】:

      将 ParamArray 参数传递给另一个需要 ParamArray 参数的函数(委托 ParamArray 参数)。 我需要委托给一个类型的函数:strf(str as string, ParamArray args() as Variant) as String 在 ParamArray 中的其他函数中接收到的参数直接传递而无需显式写入。 我发现的限制是:

      1. ParamArray() 它只能传递给另一个需要 ParamArray 的函数。
      2. ParamArray 在元素 0 处作为 Variant () 接收
      3. 当接收到第二个函数时,它会增加深度级别 我还没有找到任何令人满意的解决方案,但我编写了一个完美运行的函数,撤消添加的深度级别并返回一个带有接收参数的向量。

      代码:

      Option Explicit
      Option Base 1
      
      Public Sub PrAr1(ParamArray pa1() As Variant)
      Dim arr() As Variant
        arr = fn.ParamArrayDelegated(pa1)
        PrAr2 pa1
      End Sub
      
      Public Sub PrAr2(ParamArray pa2() As Variant)
      Dim i As Integer, arrPrms() As Variant
        arrPrms = fn.ParamArrayDelegated(pa2)
        For i = 0 To UBound(arrPrms)
          Debug.Print s.strf("i: %0 prm: %1 ", i, arrPrms(i))
        Next i
        PrAr3 pa2
      End Sub
      
      Public Sub PrAr3(ParamArray pa3() As Variant)
      Dim i As Integer, arrPrms() As Variant
        arrPrms = fn.ParamArrayDelegated(pa3)
        For i = 0 To UBound(arrPrms)
          Debug.Print s.strf("i: %0 prm: %1 ", i, arrPrms(i))
        Next i
      End Sub
      
      Public Function ParamArrayDelegated(ParamArray prms() As Variant) As Variant
      Dim arrPrms() As Variant, arrWrk() As Variant
      'When prms(0) is Array, supposed is delegated from another function
        arrPrms = prms
        Do While VarType(arrPrms(0)) >= vbArray And UBound(arrPrms) < 1
          arrWrk = arrPrms(0)
          arrPrms = arrWrk
        Loop
        ParamArrayDelegated = arrPrms
      End Function
      

      【讨论】:

      • 与其他语言相比更冗长,但它解决了我在 VBA 中似乎无法解决的问题。很好的解决方法,谢谢。
      • 不幸的是,代码不起作用(不再起作用?)当使用“Call PrAr1()”时,在“arrPrms = prms”行中,我得到一个 err.number = 5(无效调用或无效参数) (表示开头有一个空的 ParamArray)
      【解决方案5】:

      您可以在第二次调用时将其转换为 Variant

      Sub test()
          p1 "test", "banane", "birne"
      End Sub
      
      Sub p1(ParamArray keys() As Variant)
          p2 CVar(keys) '<--| pass it as a Variant
      End Sub
      
      Sub p2(keys As Variant) '<--| accept a Variant argument
          Dim key As Variant
      
          For Each key In keys
              Debug.Print key
          Next key
      End Sub
      

      【讨论】:

      • 谢谢,这对我有帮助!确认此解决方案确实有效。
      • 确实很有用。如果 p1 是您的原始函数(使用 ParamArray),并且现在您希望能够使用数组调用它:p1 变为 p2(仅更改声明),并且 p1 现在是 p2 的包装器。
      【解决方案6】:

      这是我的解决方案。请注意,它的一个限制是您只能将一个(变体)数组参数传递给 ParamArray 参数集。可能它可以泛化为处理多个传递的数组,但我还没有遇到这种需求。

      Option Explicit
      
      Sub test()
          p1 "test", "banane", "birne"
          p2 "test", "banane", "birne"
      End Sub
      
      
      Sub p1(ParamArray keys() As Variant)
          Dim TempKeys As Variant
      
          TempKeys = keys 'ParamArray isn't actually a standard Variant array, so you have to copy
                          'it to one in order for the added test/workaround in p2 to not crash
                          'Excel.
      
          p2 TempKeys 'should be the same as: p2 "test", "banane", "birne"
      End Sub
      
      Sub p2(ParamArray keys() As Variant)
          Dim key As Variant
      
          If IsArray(keys(0)) Then keys = keys(0) 'Set this routine's ParamArray parameter to be
                                                  'the array of its first element.
      
          For Each key In keys
              Debug.Print key
          Next key
      End Sub
      

      【讨论】:

      • 谢谢,您的 If IsArray(keys(0)) Then keys = keys(0) 帮助我传递了 ParamArray!
      【解决方案7】:

      我最强烈的需求之一是能够将ParamArray values() As Variant 转换为String()

      根据 OPs 的问题,我还需要能够将其他函数转发给该函数,其中其他函数有一个 ParamArray,它也需要转换为 String(),然后该函数才能继续处理。

      这里的解决方案包括一个强大的函数来安全地返回数组的大小:

      Public Function f_uas_astrFromParamArray( _
          ParamArray pr_avarValues() As Variant _
      ) As String()
        Dim astrResult() As String
        
        Dim avarTemp() As Variant
        Dim lngSize As Long
        Dim lngUBound As Long
        Dim lngIndex As Long
        
        If (IsMissing(pr_avarValues) = False) Then
          If (IsArray(pr_avarValues(0)) = True) Then
            avarTemp = pr_avarValues(0)
          Else
            avarTemp = pr_avarValues
          End If
          lngSize = f_lngArraySize(avarTemp)
          If (lngSize > 0) Then
            lngUBound = lngSize - 1
            ReDim astrResult(0 To lngUBound)
            For lngIndex = 0 To lngUBound
              astrResult(lngIndex) = CStr(avarTemp(lngIndex))
            Next lngIndex
          End If
        End If
        
        f_uas_astrFromParamArray = astrResult
      End Function
      
      'Return Value:
      '   -1 - Not an Array
      '    0 - Empty
      '  > 0 - Defined
      Public Function f_ua_lngArraySize( _
          ByRef pr_avarValues As Variant _
        , Optional ByVal pv_lngDimensionOneBased As Long = 1 _
      ) As Long
        Dim lngSize As Long: lngSize = -1 'Default to not an Array
        Dim lngLBound As Long
        Dim lngUBound As Long
        
        On Error GoTo Recovery
        
        If (IsArray(pr_avarValues) = True) Then
          lngSize = 0 'Move default to Empty
          lngLBound = LBound(pr_avarValues, pv_lngDimensionOneBased)
          lngUBound = UBound(pr_avarValues, pv_lngDimensionOneBased)
          If (lngLBound <= lngUBound) Then
            lngSize = lngUBound - lngLBound + 1 'Non-Empty, so return size
          End If
        End If
        
      NormalExit:
        f_ua_lngArraySize = lngSize
        Exit Function
        
      Recovery:
        GoTo NormalExit
      End Function
      

      【讨论】:

        【解决方案8】:

        为了在 Excel 本身调用的函数之间传递 ParamArray 变体,@JoséIborraBotia 在之前的帖子中展示的拆箱原则适用于范围列表, 但 捕获尝试将一个级别拆箱时引发的异常而不是

        像之前建议的那样测试VarType, UBound or IsArray

        允许它也适用于单个范围, 将 Excel 选择传递给自定义函数时,女巫至关重要。

        让我们找到这个拆箱函数以及它用于计算 Excel 单元格选择的任意组合的演示:

        ParamArray 拆箱函数:

        Public Function unboxPA(ParamArray prms() As Variant) As Variant
            Dim arrPrms() As Variant, arrWrk() As Variant
            Dim done As Boolean
            done = False
            arrPrms = prms
            Do While Not done
                On Error Resume Next
                  arrWrk = arrPrms(0)
                If (Err.Number > 0) Then
                  done = True
                End If
                arrPrms = arrWrk
            Loop
            unboxPA = arrPrms
        End Function
        

        用于计算任何 excel 单元格选择的拆箱:

        Function MyCountLargeCellsPA(ParamArray rangeArray() As Variant)
            Dim unboxed() As Variant
            unboxed = unboxPA(rangeArray)
            Dim n As Long
            For n = LBound(unboxed) To UBound(unboxed)
                MyCountLargeCellsPA = MyCountLargeCellsPA + unboxed(n).CountLarge
            Next
        End Function
        

        这允许使用 ParamArray 从函数到函数的任何嵌套调用,

        现在可以使用 VBA 编程了!

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-02-09
          • 2020-08-05
          • 2020-07-28
          • 1970-01-01
          • 2013-02-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多