【问题标题】:Scope of a VBA object declared and initialised in for() cycle在 for() 循环中声明和初始化的 VBA 对象的范围
【发布时间】:2012-10-22 23:02:33
【问题描述】:

我有一个“投资组合”类(它的字段之一是“股票”类)方法,代码如下:

For i = 1 To n
    Dim TempStock As New Stock
    TempStock.Set_Stock            'sets TempStock    
    ...
Next i

“Stock”是我的用户定义类,具有这样的结构、构造函数和析构函数:

Private StockName As String
Private CurDate As Date
Private BidPrice As Double
Private AskPrice As Double
Private StockDivs As Dictionary

Private Sub Class_Initialize()
    Set StockDivs = New Dictionary
End Sub
Private Sub Class_Terminate()
    Set StockDivs = Nothing
End Sub

我的意思是,在每个循环中,我都将声明并使用空字段初始化新的临时对象。但是在逐步运行我的程序之后,在我看来 Class_Terminate() 在循环结束时没有被调用。在下一步中,TempStock 的所有字段都已设置为它们在前一个循环中的设置。所以,我不明白以下几点:

1) 为什么Class_Terminate() 不调用Next i?什么时候调用?现在写对了吗?或者我是否也应该编写代码将每个非对象归档设置为空值?

2) 好的,它没有被调用,但我还有Dim TempStock As New Stock?它不应该给我双重声明错误吗?或者至少将TempStock 设置为新的空对象?

3) 有什么区别:

 Dim TempStock As New Stock

和:

 Dim TempStock As Stock
 Set TempStock = New Stock

这和我的问题有关吗?

对于我的问题的任何部分的帮助将不胜感激,谢谢!

【问题讨论】:

    标签: class vba scope


    【解决方案1】:

    VBA 中没有块级范围;所有声明 (dim) 都被提升到包含过程的顶部,并且仅在例程结束时超出范围。

    您是正确的,因为您看到的行为归结为 As New,目前该类将在循环内实例化一次,并且在过程返回时退出范围之前不会终止。

    要获得你想要的行为,你必须使用:

     Dim TempStock As Stock
     Set TempStock = New Stock
    

    因为是set 明确强制将新对象实例分配给TempStock

    当声明被提升时,Dim TempStock As New Stock 行实际上并没有立即创建Stock 的新实例,而是这种对象创建方式为每个调用插入存根代码,该代码跨越Stock 类型的变量,上面写着“如果我不是有效的参考,现在自动创建我”。

    【讨论】:

    • 但是此时第二个循环上的“旧” TempStock 对象会发生什么:设置 TempStock = 新库存?是否调用了 Class_Terminate()?
    • Set TempStock = New Stock 的第二次调用会导致新实例上的class_initialize,然后是现有实例上的class_terminate。
    • Class_Terminate 是一个在引用计数达到零时自动调用的事件。由于变量 Tempstock 在每个循环的堆上都分配了一个“新”对象,因此前一个对象被释放,并且该(已释放)对象上的析构函数被调用。 TempStock 被声明为持有一个类对象,因此不能像一个集合那样持有多个实例。
    • 啊,我明白了!我在代码中有另一个对象,它引用了 TempStock,因此在“新建”之后没有立即调用析构函数,而是在该引用终止时调用。
    【解决方案2】:

    如果你应用语法:

    Dim TempStock As Stock
    Set TempStock = New Stock
    

    析构函数被调用。
    VBA 中的对象破坏发生在被引用对象的引用计数为零(无)时。
    在这种情况下,在每个循环中,您使用相同的变量“TempStock”来实例化一个完全“Stock”对象,这会导致前一个对象从内存中释放。

    这段代码是不必要的,因为对象无论如何都会被销毁:

    Private Sub Class_Terminate()
        Set StockDivs = Nothing
    End Sub
    

    如果您在主子程序中明确将 StockDivs 设置为空,您会注意到 Class_Terminate 事件将被调用,因此在这种情况下,对象在引用时第二次收到另一个释放消息count 已经为零(否则不会调用此事件)。

    关于:

    Dim TempStock As New Stock  
    

    根据微软的说法,这两个公式是等价的:
    http://msdn.microsoft.com/en-us/library/Aa903373

    但是,等效并不意味着它们相同。
    在声明中直接使用 New 关键字确实也会实例化 Stock 对象,但正如您所注意到的,构造函数只被调用一次,而析构函数只会在循环结束时被调用。这隐含地意味着您在每个循环上一遍又一遍地使用同一个对象,同时重新分配同一个对象的属性(但没有实际释放它)。
    此公式的典型特征还在于,对象的初始化程序不会在声明时调用,而是仅在第一次使用后调用。
    在大多数用例中,两者最终都可以使用相同的结果。

    我猜你可能有用的是下面的代码。 除非您想将多个股票添加到集合中(您也可以使用字典),否则我看不到在分配属性时多次循环同一类的直接原因。

    Option Explicit
    
    Sub Stocks()
    
    Dim oCollection         As Collection
    Dim TempStock           As Stock
    Dim lCnt                As Long
    
    
    Set oCollection = New Collection
    
    For lCnt = 1 To 5
    
        Set TempStock = New Stock
    
        TempStock.Set_Stock ("Stock_" & lCnt)       'sets TempStock name eg. 
        oCollection.Add TempStock
    Next lCnt
    
    End Sub
    

    您可以将多个股票添加到一个集合中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-05
      • 1970-01-01
      • 2013-10-28
      相关资源
      最近更新 更多