我在网络和我自己的项目中无处不在地看到具有以下模式的代码:
Sub Func()
Application.EnableEvents = False
' some code
Application.EnableEvents = True
End Sub
由于VBA
对象的生命周期似乎是确定性的,我认为我可以像C++
中那样用所谓的哨兵对象替换这个模式,这样就会出现异常退出的问题( err.raise)可以自动解决。
VBA
的新手,甚至不知道何时通过引用传递对象。理想情况下,我希望代码看起来像这样:
Sub Func()
dim Sentry
Set Sentry = CreateSentry(Application.EnableEvents,False)
' From now on we should not need to care if the variable was actually
' True or False beforehand, what kind of error handling is used in this function, etc.
End Sub
答案 0 :(得分:5)
Application.EnableEvents
不是变量,而是属性。你不能在VB(A)中通过这样的引用传递属性,编译器将创建当前属性值的临时副本,并且你的岗哨将在副本上“关闭”。
要以这种方式管理对象属性,您可以这样做:
创建一个类,例如命名它SentryForPropertiesVariant
并使用类似的代码:
Option Explicit
Private m_Obj As Object
Private m_PropertyName As String
Private m_OldValue As Variant
Public Sub Init(ByVal obj As Object, ByVal PropertyName As String, ByVal NewValue As Variant)
Set m_Obj = obj
m_PropertyName = PropertyName
m_OldValue = CallByName(obj, m_PropertyName, VbGet)
CallByName m_Obj, m_PropertyName, VbLet, NewValue
End Sub
Private Sub Class_Terminate()
If Not m_Obj Is Nothing Then
CallByName m_Obj, m_PropertyName, VbLet, m_OldValue
End If
End Sub
然后使用它:
Dim s As SentryForPropertiesVariant
Set s = New SentryForPropertiesVariant
s.Init Application, "EnableEvents", False
您还可以在模块中使用辅助功能:
Public Function CreateSentry(ByVal obj As Object, ByVal PropertyName As String, ByVal NewValue As Variant) As SentryForPropertiesVariant
Set CreateSentry = New SentryForPropertiesVariant
CreateSentry.Init obj, PropertyName, NewValue
End Function
此时使用变得更简单:
Dim s As SentryForPropertiesVariant
Set s = CreateSentry(Application, "EnableEvents", False)
在这种情况下,您可能希望将Public Sub Init
替换为Friend Sub Init
。
如果您计划将您的sentry类存储在共享加载项(.xla)中,则无论如何都必须具有此类辅助函数,因为无法通过驻留在其他工作簿中的代码创建加载项中定义的类,因此解决方案是在与创建实例的类相同的工作簿中定义一个函数,并将其返回给外部调用者。
最后,使用With
(类似于C#的using
)控制此类哨兵的生命周期很方便:
With CreateSentry(Application, "EnableEvents", False)
'Here EnableEvents is False
End With
'Here it's True
但是,在执行此操作时,您应该牢记With
仅类似于 using
。如果你使用GoTo
过早地跳出它,End With
语句will not be executed,这意味着持有sentry实例的临时变量将存活到过程的结尾,并且属性不会在此之前恢复到原来的价值。
所以不要跳出那些街区。如果您必须,请在End With
之前创建一个标签并跳转到该标签。
答案 1 :(得分:0)
类模块可以用于此目的,因为您可以指定在创建类模块实例时运行的代码,重要的是当对象上的引用计数降为零时。
具体来说,您可以将代码放入
Private Sub Class_Initialize()
和
Private Sub Class_Terminate()