【问题标题】:How do I correctly work with a collection of VBA Class Modules?如何正确使用 VBA 类模块的集合?
【发布时间】:2017-06-22 14:09:29
【问题描述】:

我想创建一个机场集合。

一个机场有很多参数,但为了简单起见,我们假设一个机场类在 Airport Class Module 中是这样定义的:

'Class Airport
 Public name As String ' Stores name of the airport
 Public flights As Long ' Stores number of flights in that airport

然后我的模块相当大,但这是我从 Excel 文件中读取列并将值存储在机场集合中的部分,删除重复的:

Dim airports As Collection
Set airports = New Collection

'Putting airports in collection
                Dim c As Range
                For Each c In wsToCheck.Range("D:D")
                On Error Resume Next
                Dim airport As New Airport
                airport.name = c.Value
                airports.Add airport, c.Value
                On Error GoTo 0
                Next

如果我在中间做

Debug.Print airport.name

我知道名字,但是当我知道时

Debug.Print airports(1).name

什么都没有打印(但也没有错误)。

我之前使用了一个字符串集合,它正在工作。但我现在每个机场需要不止一个字符串。

我的代码有什么问题?我在使用集合吗?

【问题讨论】:

  • 您在代码中的什么时候执行了 Debug.Print?当我运行这段代码时,它按预期运行。
  • 我也会创建一类机场,所以你可以说airports.AirPortExists getAirportRunways(strAiportCode) 上面的代码将存在于构造中,或者说.populateAiports(rng as excel.range)

标签: excel vba class collections


【解决方案1】:

您的代码有两个问题。

第一个可能是创建一个包含数百万个项目的Collection,因为您正在迭代的范围是所有列 D (D:D)。这需要绑定。

第二个问题是您的变量名称airport 与您的类名称Airport 完全相同。这很容易混淆 VBA,因此您需要为其中一个选择不同的名称。

这是一个有效的例子:

Option Explicit

Sub test()
    Dim wsToCheck As Worksheet
    Set wsToCheck = ThisWorkbook.Sheets("Sheet1")

    Dim airportNames As Range
    Set airportNames = wsToCheck.Range("D1:D10")

    Dim airports As Collection
    Set airports = New Collection

    'Putting airports in collection
    Dim i As Long
    Dim c As Range
    For Each c In airportNames
        Dim thisAirport As Airport
        'Debug.Print c.Address & "=" & c.Value
        Set thisAirport = New Airport
        thisAirport.name = c.Value
        thisAirport.flights = i
        i = i + 1
        airports.Add thisAirport
    Next

    'now print them out
    For i = 1 To airports.Count
        Debug.Print airports(i).name & ", " & airports(i).flights
    Next i

End Sub

【讨论】:

  • 这行得通,谢谢。但是,我不知道每个文件中的机场数量,20-30000。我一直在用 D:D 做它,它不会崩溃。会不会导致一些问题?
  • 作为一般规则,您应该使用 VBA 来确定该列中有多少个机场。你需要弄清楚最后一行是什么来设置你的范围,例如:Dim lastRow as Long, lastRow = wsToCheck.Range("A" & wsToCheck.Rows.Count).End(xlUp).Row
  • 这样做确实大大提高了我的程序速度。从 2 分钟到 20 秒或多或少。很好的建议。
【解决方案2】:

除了@PeterT 提到的问题之外,您的代码中最大的问题是新的Airport 对象的创建方式。我最好说Airport 对象,因为实际上没有对象,只有一个对象。

这是由声明机场变量的方式引起的:Dim airport As New Airport。在VBA-DocumentationDim 语句中我们可以找到这里只创建一个对象的原因:

New 关键字可以隐式创建对象。如果您使用新 声明对象变量时,对象的新实例是 在第一次引用它时创建,因此您不必使用 Set 用于分配对象引用的语句。

所以Dim New 确保在第一次使用变量airport 时创建新实例。但就是这样。没有创建其他实例,仅创建第一个。在每个循环中,只有一个实例被更改并添加到集合中。因此,您的集合包含对同一机场对象的引用。

但是你需要所有的机场对象,而不仅仅是一个。所以New 必须与Set 一起使用。文档说:

当 New 与 Set 一起使用时,它会创建一个类的新实例


考虑以下代码(范围 D1:D3 包含值 AAAA、BBBB、CCCC)

Dim airports As Collection
Set airports = New Collection
Dim c As Range

For Each c In Worksheets(1).Range("D1:D3")
    Dim Airport As New Airport
    Airport.name = c.Value
    airports.Add Airport
Next

Dim airports2 As Collection
Set airports2 = New Collection
Dim airport2 As Airport
For Each c In Worksheets(1).Range("D1:D3")
    Set airport2 = New Airport
    airport2.name = c.Value
    airports2.Add airport2
Next

Debug.Print "Airports:"
For Each airport2 In airports
    Debug.Print airport2.name
Next

Debug.Print vbNewLine

Debug.Print "Airports2:"
For Each airport2 In airports2
    Debug.Print airport2.name
Next

输出

Airports:
CCCC
CCCC
CCCC


Airports2:
AAAA
BBBB
CCCC

This question 也是同样的问题。

【讨论】:

    猜你喜欢
    • 2021-03-13
    • 1970-01-01
    • 1970-01-01
    • 2021-08-24
    • 2014-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多