特定对象的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
,可以有效地用于Set
和Range/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变体?的主要内容,如果未能解决你的问题,请参考以下文章