【问题标题】:Is it possible to limit the args of a ParamArray in a VBA Class Sub or Property?是否可以在 VBA 类子或属性中限制 ParamArray 的参数?
【发布时间】:2017-04-12 17:04:38
【问题描述】:

我正在创建一个 vba excel 类,并试图弄清楚如何允许用户设置应用于变量/字段的值组。从我读到的内容来看,除非您打算要求它们,否则不可能在属性上具有多个参数。我想要做的是使它只有第一个参数是必需的,而剩余的参数数量是可选的。当需要使后续参数可选时,我似乎建议使用 sub 。例如,当我设置以下子时,这很好用

Public Sub SetCategories(CategoryOne As String, Optional CategoryTwo As String)
    pCategories(0) = CategoryOne
    pCategories(1) = CategoryTwo
End Sub

由于只有两个参数,我只需要一个,上述设置就足够了。但是,我还有其他属性需要最多允许 30 个值,我真的不想硬编码 1 个必需值和 29 个可选值,因为这看起来像是糟糕的设计。那是我开始研究 ParamArray 选项的时候。 ParamArray 非常适合这种情况,除了我遇到了一个问题。 ParamArray 允许任意数量的参数,我需要能够将其限制为 29 个(加上所需的一个)。那么,有没有办法限制 ParamArray 的上限以只允许 Sub 的这么多参数?

类似下面的东西

Public Sub SetKeywords(KeywordOne As String, Optional ParamArray KeywordTwoThroughThirty(29) as String)

End Sub

这可能吗?如果是这样,怎么做?如果没有,你会建议我如何处理这个场景?

【问题讨论】:

  • 一个属性不能有多个参数,除非您打算要求它们 - IMO 使用属性参数来创建 索引属性 是语言滥用。将其作为一种方法的良好决策。
  • 30 个可选参数是一个糟糕的设计?请告诉写Application.Run的人! =)
  • 很高兴再次见到你@Mat'sMug :) 我需要帮助已经有一段时间了。那么,您认为我应该如何处理提供最多 30 个插槽的情况?我希望它像其他参数在可选时显示在函数上一样显示。键入test.SetKeywords( 时的示例我希望它看起来像KeywordOne As String, [KeywordTwoThroughThirty2],[KeywordTwoThroughThirty3]...[KeywordTwoThroughThirty30])。这有可能吗?如果没有,您是否建议提供另一种方法来提供 30 个可选参数?
  • 如果你想要那种 IntelliSense,那么你需要做Application.Run 所做的事情——没错,30 个显式可选参数。然后让调用者能够使用DoSomething "foo", arg10:="bar" 来提供一个可选参数并破坏所有期望第一个可选参数为arg1 的内容;-)
  • @CaffeinatedCoder - 你的用户是谁?你能期望他们能够传递一个数组,那么你的函数只能使用非空元素吗?

标签: arrays vba excel parameters


【解决方案1】:

这是不可能的。 ParamArray,顾名思义,给你一个参数数组

A ParamArray 只能通过引用传递(隐式 - 显式 ByRef 不会编译),并且只能是 Variant 项目的数组,并且不能是 Optional - 所以这可以' t 编译:

Optional ParamArray KeywordTwoThroughThirty(29) as String

相反,您只需这样做:

ParamArray KeywordTwoThroughThirty()

可以做的是,当调用者提供了太多参数时,使用 保护子句 向调用者抛出错误 - 有趣的是,似乎 Option BaseParamArray 数组的基本边界没有影响,无论Option Base 设置如何,它似乎都是0。这使得验证指定了多少参数变得更加简单:

Const maxArgs As Long = 30
If UBound(KeywordTwoThroughThirty) + 1 > maxArgs Then
    Err.Raise 5, "SetKeywords", _
                 "Too many arguments were specified. Maximum is " & maxArgs & "."
End If

现在调用者可以传递多达 30 个可选参数;如果它们通过 31 或更多,它们将不得不处理运行时错误 5“无效的过程调用或参数”,这在这里特别合适。


IntelliSense 不会帮助您的调用者知道他们传递了多少参数:

如果您希望调用代码的 IntelliSense 看起来像这样:

...那么您需要为您可以获得的 30 个参数中的每一个指定显式可选参数

Application.Run 就是这样做的。

不过,我建议将它们全部传递给 ByVal

您可能遇到的一个问题是聪明的调用者可以这样做:

DoSomething arg1:="foo", arg14:="bar"

现在如果你的实现假设一个空的arg2 意味着一个空的arg3 和一个空的arg14,那么你就会有错误......而ParamArray 解决方案并不完全允许用户用命名参数搞砸了,你的实现可以在需要时迭代数组,这取决于它的目的。


不过我会推荐一种不同的方法。

Public Sub SetCategories(CategoryOne As String, Optional CategoryTwo As String)
    pCategories(0) = CategoryOne
    pCategories(1) = CategoryTwo
End Sub

如果调用者无论如何都不调用SetCategories,你的类将没有pCategories(0) 的值 - 所以我会采用你的第一个想法:索引属性:

Public Property Get Category(ByVal index As Long) As String
    Category = pCategories(index)
End Property

Public Property Let Category(ByVal index As Long, ByVal value As String)
    pCategories(index) = value
End Property

然后暴露一些Init方法:

Public Sub Init(ByVal category1 As String)
    ReDim pCategories(0 To 29) As String
    pCategories(0) = category1
End Sub

这样调用者可以这样做:

With foo
    .Init "bar"
    .Category(1) = "smurf"
    .Category(2) = "blah"
    '...
End With

它们可以使用循环和其他数据结构来访问和操作 30 个封装的项目;如果 VBA 尝试使用超过 30 个元素,VBA 将处理抛出 subscript out of range 错误。

【讨论】:

  • 非常聪明的替代方法,并且一如既往的很好解释的方法。非常感谢,谢谢:)
猜你喜欢
  • 2011-02-22
  • 2012-11-29
  • 1970-01-01
  • 2015-03-25
  • 2013-07-31
  • 1970-01-01
  • 2021-12-20
  • 2016-09-22
  • 1970-01-01
相关资源
最近更新 更多