特定对象的VBA变体?

Posted

技术标签:

【中文标题】特定对象的VBA变体?【英文标题】:VBA Variant to certain object? 【发布时间】:2017-03-24 06:29:42 【问题描述】:

我开始在 VBA (Excel AddIn --> xlam) 中使用Collection,但发现它很难使用。除了变体之外,我无法检索我放入的任何内容。例如,在 Java 中,如果你创建了一个Collection,你会为它分配一个类型,比如Collection<MyPerfectObjectThatStoresSomeData>,所以当我 foreach 时,我知道我得到了我的对象并且可以像那样使用它。当我想在 VBA 中做同样的事情时,我会返回 Variant 类型,但我什至无法将其转换为我的类型。

示例。

假设我想从Collection 获取最后一个条目,我填写如下:

Private Function FindTargets(sheet As Worksheet, target As String) As Collection
    Dim result As New Collection
    Dim aCell As range
    Dim i As Long
    For i = 1 To 10456
      Set aCell = sheet.Rows(i).Find(What:=target, LookIn:=xlValues, _
            LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
            MatchCase:=False, SearchFormat:=False)
        If Not aCell Is Nothing Then
             MsgBox "Value Found in Cell " & aCell.Address & _
            " and the Cell Column Number is " & aCell.Column
            result.Add aCell
        End If
    Next i

    Set FindTargets = result
End Function

之后,我想遍历结果,但我无法在没有错误的情况下获取最后一个条目:

Dim targetCollection As Collection
Set targetCollection = FindTargets(targetSheet, "some text")
Dim target As range
target = targetCollection.Item(targetCollection.Count - 1)

我得到错误:

Run-time error '91':
Object variable or With block variable not set

如果我尝试

Set target = targetCollection.Item(targetCollection.Count - 1)

我明白了:

Run-time error '1004':
Application-defined or object defined error

我只能循环并从Collection 获取条目到Variant 以外的类型吗?

编辑:更具体地说,此代码需要范围的坐标,但在Variant 中,我得到了单元格的文本。我考虑过创建一个新类,例如 CellProperties 具有文本和坐标属性并将其放入集合中,但同样,我无法从 Collection 中检索除 Variant 之外的任何内容。

【问题讨论】:

最后一项将使用Set target = targetCollection.Item(targetCollection.Count) 找到。您是否可能只向集合中添加了一项,因此您的错误仅仅是因为您试图访问targetCollection.Item(0)? (我使用您的函数的倒计时版本对其进行了测试,它只是将 A1:A100 中的 100 个单元格添加到集合中。) 集合中有两个元素。 VBA 集合是否从 1 开始严重索引? 当我在 Watch 窗口中查看一个集合时,它肯定会出现。我将暂时将我的测试代码发布为“答案”,以便您查看它是否适合您。 哦,谢谢,我不知道我们有一个监视窗口。对于它说 Variant/Object/Range 的类型,但我无法读取任何没有错误的元素。 监视窗口应将targetCollection 显示为Collection/Collection,将target 显示为Range/Range。啊,但是Collection/Collection 的每一项都是Variant/Object/Range,可以有效地用于SetRange/Range 【参考方案1】:

您的错误似乎是由于假设 Collection 的项目是从零开始的,但 Collection 中的第一个项目的索引为 1

下面是我用于测试的FindTargets 的精简版本,以及一个返回添加到Collection 的最后一个单元格的Address 的测试子例程:

Private Function FindTargets(sheet As Worksheet, target As String) As Collection
    Dim result As New Collection
    Dim i As Long
    For i = 1 To 100
        result.Add Cells(i, 1)
    Next i

    Set FindTargets = result
End Function

Sub test()
Dim targetCollection As Collection
Set targetCollection = FindTargets(Worksheets("Sheet1"), "some text")
Dim target As Range
Set target = targetCollection.Item(targetCollection.Count)
MsgBox target.Address 'will display $A$100
End Sub

【讨论】:

@appl3r 但是如果你的 targetCollection.Count 是 2,那么 Count - 1 应该没问题。 @appl3r - 顺便说一句 - 你说“但在变体中我得到了单元格的文本” - 只是尝试像 MsgBox target 这样的东西(它使用范围的默认属性是 @ 987654328@,因此相当于说MsgBox target.Value)? 我想我在这个任务之后还有另一个任务引发了同样的异常。 如果你在断点上悬停在变量上,你会得到一个打印它的值的弹出窗口,虽然它是一个字符串,但使用手表我可以精确地检查它。 @appl3r 这是因为可爱的 默认成员 功能,在Range 的情况下返回它的Value;在Collection 的情况下,默认成员是Item 属性,所以myCollection.Item(i)myCollection(i) 完全相同 - 尽管我建议避免隐式默认成员引用,因为它们使代码“更容易type”,但更难理解并且经常令人困惑,尤其是当涉及到 Variant 中的对象引用时。【参考方案2】:

VBA-Collection 是可以包含异构对象的数据结构。因此,与 Collection Of MyPerfectObjectThatStoresSomeData 集合确保可以添加特定数据类型的对象不同,在 VBA-Collection 中,我们可以在集合中存储任何内容,因此它可以同时包含异构对象类型。但在您的情况下,集合实际上包含Variant\Object\Range 类型的对象。这意味着它包含Variant 类型和子类型Range 的对象。 VBA-Collection 可以枚举,它具有Item 方法,可用于访问集合中第一项具有1 索引的项目。

在你的情况下,你可以做例如这个:

targetCollection.Add "Banana"
targetCollection.Add ActiveSheet
targetCollection.Add Excel.Application

Dim itm As Variant
For Each itm In targetCollection
    If TypeName(itm) = "Range" Then _
        Debug.Print itm.Address
Next itm

安全地获取最后一个元素(如果最后一个元素的类型为Range):

Dim lastElement As Range
If targetCollection.Count > 0 Then _
    Set lastElement = targetCollection.Item(targetCollection.Count)

附带说明:为什么不使用Dim As New。

【讨论】:

所以我可以在不强制转换的情况下检查Variant 上的.Address?有趣 Variant 是变体,这意味着它可以包含任何内容。如果它的sub-type 有一个属性Address 那么你可以使用它。 Per “为什么不使用 Dim As New” - 以这种方式实例化对象存在细微差别 - 它使用预先声明的实例。这允许对象的自动实例化,降低编译代码的性能(由于检查对象是否在使用它的任何地方都被实例化),并且很容易导致编码错误。除非您要求对象自动实例化,否则最好避免。 顺便说一句,强制转换的原因是Variant 没有子类型Range - 它有一个子类型IDispatch。直接从 Variant 按名称调用成员会导致对早期绑定对象的后期绑定调用。 @appl3r 绝对。 Variant(和Object)成员调用在运行时(后期绑定)解决,类似于像 javascript 这样的鸭式语言,或者 C# 的 dynamic 的工作方式(即运行时错误 438“对象如果调用itm.IDontExist,则会引发“不支持此属性或方法”,但代码将编译得非常好)。想要在 VBA 中使用强类型泛型几乎是天方夜谭,VBA 对泛型一无所知。

以上是关于特定对象的VBA变体?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 django rest 框架中仅使用特定变体对象将项目添加到愿望清单?

VBA 编码选择特定的参考对象库

VBA-基于特定单元格隐藏/显示多个对象

VBA - 另存为后工作簿的对象会发生啥?

VBA:如何使变体为空?

布尔数组和变体类型 vba