Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/vba/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/excel/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
VBA中的哨兵对象_Vba_Excel - Fatal编程技术网

VBA中的哨兵对象

VBA中的哨兵对象,vba,excel,Vba,Excel,我在网络和我自己的项目中随处可见具有以下模式的代码: Sub Func() Application.EnableEvents = False ' some code Application.EnableEvents = True End Sub 由于VBA对象的生命周期似乎是确定的,我想我可以像在C++中那样,用所谓的sentry对象来代替这个模式,这样异常退出(err.raise)的问题就可以自动解决了 但是怎么做呢?我不太清楚,因为我是VBA新手,甚至不知道什

我在网络和我自己的项目中随处可见具有以下模式的代码:

Sub Func()
     Application.EnableEvents = False
     ' some code
     Application.EnableEvents = True
End Sub
由于
VBA
对象的生命周期似乎是确定的,我想我可以像在
C++
中那样,用所谓的sentry对象来代替这个模式,这样异常退出(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

类模块可用于此目的,因为您可以指定在创建类模块实例时运行的代码,更重要的是,当对象上的引用计数降至零时运行的代码

具体来说,您可以将代码放入

Private Sub Class_Initialize()


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)中,则无论如何都必须具有此类帮助程序函数,因为外接程序中定义的类不能从其他工作簿中的代码创建,因此,解决方案是在创建实例并将其返回给外部调用方的类所在的工作簿中定义一个函数


最后,使用
(类似于C#的
使用
)可以方便地控制此类哨兵的寿命:

但是,在执行此操作时,应记住
With
仅类似于
use
。如果使用
GoTo
过早跳出,则
将以
语句结束,这意味着保存sentry实例的临时变量将一直存在到过程结束,并且在此之前属性将不会恢复到其原始值


所以不要跳出那些街区。如果一定要这样做,请在
结尾之前创建一个标签,然后跳转到该标签。

OP已经声明(
VBA对象的生存期似乎是确定的
)。问题是将某些内容传递给类,这些内容可用于以后还原外部值(VB没有显式指针)。我将暂时搁置,看看它是否对OP有用。我应该在我的需求中更具体一些,因为我需要一种技术来定义一个适用于的通用类(如果可能的话)任何类型的属性,而不仅仅是一种。我认为你的答案不值得投反对票,只有我一个人,对不起。谢谢,在这么短的时间内,这是一个荒谬的完美答案!我确认这完全符合我的预期,唯一的缺点是由于属性名以字符串形式传递,因此只能在运行时捕获错误。也许我应该将常用的属性名定义为常量。对像这样的哨兵使用
简直是天才!一定会记住这个!
Dim s As SentryForPropertiesVariant
Set s = CreateSentry(Application, "EnableEvents", False)
With CreateSentry(Application, "EnableEvents", False)
  'Here EnableEvents is False
End With

'Here it's True