用户定义类型与类速度

时间:2014-07-08 13:22:41

标签: vba class user-defined-types

我有一张工作簿,其中包含两张我需要执行操作的数据。我开始直接从工作表中处理数据,但很快发现它非常慢,所以更改了将工作表读入两个数组(在两个单独的方法中调用Workbook_Open)。

我为每张工作表上的数据创建了一个用户定义的类型,然后我发现我无法将这些添加到集合或脚本字典中,因此我将它们转移到了类中。

所以现在我有一个名为CDealerData的类,每个类都有4个私有字段和公共属性。问题是,将数据读入数组的执行时间是我使用类型时的两倍。这就是它是怎么回事,或者我做错了什么。

类别:

Option Explicit

Private pBAC As String
Private pAccountNumber As String
Private pYear As Integer
Private pUnits As Variant


Public Property Get BAC() As String
    BAC = pBAC
End Property
Public Property Let BAC(Value As String)
    pBAC = Value
End Property


Public Property Get AccountNumber() As String
    AccountNumber = pAccountNumber
End Property
Public Property Let AccountNumber(Value As String)
    pAccountNumber = Value
End Property


Public Property Get Year() As String
    Year = pYear
End Property
Public Property Let Year(Value As String)
    pYear = Value
End Property


Public Property Get Units() As String
    Units = pUnits
End Property
Public Property Let Units(Value As String)
    pUnits = Value
End Property
Option Explicit

Private pBAC As String
Private pAccountNumber As String
Private pYear As Integer
Private pUnits As Variant


Public Property Get BAC() As String
    BAC = pBAC
End Property
Public Property Let BAC(Value As String)
    pBAC = Value
End Property


Public Property Get AccountNumber() As String
    AccountNumber = pAccountNumber
End Property
Public Property Let AccountNumber(Value As String)
    pAccountNumber = Value
End Property


Public Property Get Year() As String
    Year = pYear
End Property
Public Property Let Year(Value As String)
    pYear = Value
End Property


Public Property Get Units() As String
    Units = pUnits
End Property
Public Property Let Units(Value As String)
    pUnits = Value
End Property

模块:

Option Explicit

Public NumberOfYears As Integer

Public DealersData() As CDealerData

Public Sub ReadDealerData()

    '** Reads the contents of RawData into an Array
    '** of custom type DealerData, defined above

    Dim MyDealerData As CDealerData
    Dim LastRow As Long
    Dim i As Long
    Dim j As Long

    LastRow = SheetRawData.UsedRange.Rows.Count

    ReDim DealersData(LastRow * NumberOfYears)

    For i = 0 To LastRow
        For j = 0 To NumberOfYears - 1 'Year columns
            Set MyDealerData = New CDealerData

            MyDealerData.BAC = SheetRawData.Cells(i + 2, 1).Value
            MyDealerData.AccountNumber = SheetRawData.Cells(i + 2, 3).Value
            MyDealerData.Year = j + 1
            MyDealerData.Units = CDec(SheetRawData.Cells(i + 2, 4 + j).Value) 'Assign column based on j

            Set DealersData(i) = MyDealerData
        Next j
    Next i

End Sub

3 个答案:

答案 0 :(得分:5)

由于种种原因,UDT比以这种方式使用类要快得多。

  1. UDT是内存中的结构,可以直接写入数据
  2. Class将具有Let和Get属性,这些属性是执行并具有一些开销的函数
  3. 类的创建和销毁会增加一点开销,但在你的情况下没有什么值得注意的
  4. 为了提高性能,您可以考虑使用公共变量而不是私有属性,但是这可能会失败使用类的目的。

    • 如果您希望将其简单地用作数据容器,那么最好使用用户定义的数据类型。
    • 如果您希望使用类特定功能进一步操作此数据,那么Class方法更好

    此外,加快速度的一般方法是尽可能少地访问电子表格

    例如代码如下

    For i = 1 to 10
       Variable = Worksheets("Sheet1").Range("A1").Cell(i,1).Value
    Next i
    

    可以替换为

    Dim VariantArray as Variant
    VariantArray = Workeheets("Sheet1").Range("A1:A10")
    
    ' Now VariantArray(0,0) has the first element, (1,0) has the second, etc.
    

    关于性能分析的说明:请注意@ BlackHawk在下面的评论中的建议,使用MicroTimer工具。它非常有用,可以隔离部分代码并将性能影响发现到非常精确的级别。

    此外,虽然对于任何平台都是如此,但VBA性能有时可能会不一致,具体取决于Excel目前的资源压力,因此,即使MicroTimer精确,也可能无法准确代表您可能需要考虑在不同时间运行循环,以正确评估代码不同部分的影响。

答案 1 :(得分:1)

使用此语法通过一个操作Dim x() as Variant : x = Range("A1").Resize(40,20).Value读取整个数组。

这将读取从A1开始的40行和20列的单元格到Variant(,)的2D数组。

你可以循环遍历这个数组,将值放入用户类型中,它会更快,比如DealersData(i*NumberOfYears+j).BAC = x(2*i-1,j),或者你的事情有条理。

答案 2 :(得分:0)

第一个我会优化CDealerData-Class,如下所示:

Private pUnits As Decimal 'instead of Variant, the internal mapping uses Time
Private pYear As Long 'instead of integer because outside of the Class you calc with Long

此外,我建议您创建一个方法来将数据设置为一行而不是可写属性:

Public Sub SetData(BAC As String, AccountNumber as String, Year as Long, Units as Decimal)
    pBAC = BAC 
    pAccountNumber = AccountNumber
    pYear = Year
    pUnits = Units
End Sub

您的模块中的用法如下所示:

For i = 0 To LastRow
    For j = 0 To NumberOfYears - 1 'Year columns
        Set MyDealerData = New CDealerData
        MyDealerData.SetData(SheetRawData.Cells(i + 2, 1).Value, SheetRawData.Cells(i + 2, 3).Value,  j + 1, CDec(SheetRawData.Cells(i + 2, 4 + j).Value))
        'Assign  column based on j

        Set DealersData(i) = MyDealerData
    Next j
Next i

同样使用Class,您可以使用Collection,并且您不需要为阵列提供ReDim。

希望它有所帮助。

干杯 安迪