【问题标题】:Check whether value is iterable vba检查值是否是可迭代的 vba
【发布时间】:2023-03-10 21:15:01
【问题描述】:

在许多语言中,可以检查一个对象是否是可迭代的,我如何为 VBA 做到这一点?

我可以试试:

Function isIterable(obj As Variant) As Boolean
    On Error Resume Next
    Dim iterator As Variant
    For Each iterator In obj
        Exit For
    Next
    isIterable = Err.Number = 0
End Function

但我想知道是否有内置或更好的方法?

【问题讨论】:

  • 请注意,虽然您的函数支持数组,但数组应该使用For 循环而不是For Each 进行迭代(出于重要的性能原因)。调用代码应改为使用 IsArray 测试该变体。
  • 但由于他只是迭代列表中的第一个项目来测试可迭代性(现在是一个词!),这并不重要,@Mat'sMug。
  • @FreeMan 不在 that 函数中 - 但如果某些代码正在调用此函数以发现是否可以使用 For Each 循环迭代某些内容,并且该函数需要一个数组并说“是的,当然没问题”,那么假设调用代码将分支到For Each 循环似乎是合理的,无论obj 是什么。如果那是一个数组,那么它的效率就太低了。
  • 所以,@Mat'sMug,一旦确定对象是可迭代的,就应该进一步测试它是否是一个数组,然后正确循环,而不是测试“那个变体”用IsArray 代替".

标签: vba excel


【解决方案1】:

有没有内置函数没有。

有没有更好的办法?

我会这样做:

Function isIterable(obj As Variant) As Boolean

    On Error GoTo isIterable_Error

    Dim iterator As Variant

    For Each iterator In obj
        isIterable = True
        Exit Function
    Next

isIterable_Error:

End Function

因为在同一行上放两次= 有点太多了。

【讨论】:

  • 为什么On Error Goto 0,在退出函数时不是自动的吗?另外,不会引发错误跳过该行吗?我认为您的方法也更好,因为它不涉及调用Err.Number
  • @Greedo - On Error GoTo 0 是一种习惯(并且是 MZTools 的内置函数,我用它来处理错误)。事实上,没有它它也会运行。并且提出错误会跳过它,是的。事实上,这条线在任何情况下都无法到达:)
  • 不得不不同意将= 放两次太多了。我发现更容易想到 是错误返回错误吗?将该答案放在变量中`。与 Boolean = Not Boolean 相同的想法将 TRUE 转换为 FALSE 并将可见转换为不可见。
  • @DarrenBartrup-Cook 我会选择isIterable = (Err.Number = 0) ;-)
  • @KostasK。这是。但它修复了我的强迫症。由于在 VBA 中这样使用的括号本质上意味着“将此表达式作为一个值来评估”,因此在此处使用它们有助于区分赋值 = 运算符和比较 = 运算符 - 在其他语言中赋值和比较运算符不同(例如= vs ==),我不会放括号。
【解决方案2】:

我不认为它比 Vityata 的功能更好,但只是作为替代:

Function isIterable(obj As Object) As Boolean

    On Error Resume Next

    isIterable = TypeName(CallByName(obj, "_NewEnum", VbGet)) = "Unknown"
    If Not isIterable Then isIterable = TypeName(CallByName(obj, "_NewEnum", VbMethod)) = "Unknown"

End Function

【讨论】:

  • 它不能处理的一件事(OP 的代码可以处理)是数组 - 采用 Variant 参数并将 If IsArray(obj) Then isIterable = True : Exit Function 粘贴在顶部,你就可以开始了。此外,自定义集合类不能有 _NewEnum 成员(下划线前缀在 VBA 标识符中是非法的)。我公开了一个NewEnum 属性获取器(不确定名称是否与VB_UserMemId = -4 成员属性一样重要),因此使用"NewEnum" 进一步检查成员名称将起作用......但不是100% 可靠。 真正做到这一点的唯一方法是使用一些反射 API。
  • 您可以处理_ 例如MsgBox TypeName(aCollection.[_NewEnum])
  • @AlexK。是的,这就是自定义集合调用其内部集合的枚举器的方式......我的意思是,自定义集合很可能有一个Public Property Get NextItem() As IUnknown 和一个成员属性NextItem.VB_UserMemId = -4,并且在那里,该函数将有一个假阴性。 IOW,没有使用能够查询成员以获取该特定属性值的反射 API,最好的解决方案是 OP 所拥有的。
  • 这绝对是 COM 的乐趣。 _NewEnum 是对象的正确方法。请注意,您不适用于 Array("foo","bar","barry")
猜你喜欢
  • 1970-01-01
  • 2013-09-23
  • 1970-01-01
  • 2017-04-05
  • 2021-01-13
  • 2017-02-23
  • 2011-01-04
  • 2019-12-09
  • 2023-04-10
相关资源
最近更新 更多