VBA中的Sentry对象

mni*_*ish 1 excel vba excel-vba

我在网络和我自己的项目中无处不在地看到具有以下模式的代码:

Sub Func()
     Application.EnableEvents = False
     ' some code
     Application.EnableEvents = True
End Sub
Run Code Online (Sandbox Code Playgroud)

由于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
Run Code Online (Sandbox Code Playgroud)

GSe*_*erg 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
Run Code Online (Sandbox Code Playgroud)

然后使用它:

Dim s As SentryForPropertiesVariant
Set s = New SentryForPropertiesVariant

s.Init Application, "EnableEvents", False
Run Code Online (Sandbox Code Playgroud)

您还可以在模块中使用辅助函数:

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
Run Code Online (Sandbox Code Playgroud)

在哪一点使用变得更简单:

Dim s As SentryForPropertiesVariant
Set s = CreateSentry(Application, "EnableEvents", False)
Run Code Online (Sandbox Code Playgroud)

在这种情况下,你可能要替换Public Sub InitFriend Sub Init.

如果您计划将您的sentry类存储在共享加载项(.xla)中,则无论如何都必须具有此类帮助函数,因为无法通过驻留在其他工作簿中的代码创建加载项中定义的类,因此解决方案是还要在与创建实例的类相同的工作簿中定义函数,并将其返回给外部调用者.


最后,用With(类似于C#using)来控制这些哨兵的生命是很方便的:

With CreateSentry(Application, "EnableEvents", False)
  'Here EnableEvents is False
End With

'Here it's True
Run Code Online (Sandbox Code Playgroud)