Vba 在过程结束和错误处理程序中编写结束语句的最佳实践是什么?

Vba 在过程结束和错误处理程序中编写结束语句的最佳实践是什么?,vba,excel,Vba,Excel,我发现自己在过程结束时和错误处理程序中都编写了一些命令。最重要的是,我发现自己在所有函数中都写了这些行: Application.Cursor = xlDefault Application.ScreenUpdating = True ErrHandler: MsgBox ("An unforseen problem has occured. Please contact support.") Application.Cursor = xlDefault

我发现自己在过程结束时和错误处理程序中都编写了一些命令。最重要的是,我发现自己在所有函数中都写了这些行:

    Application.Cursor = xlDefault
    Application.ScreenUpdating = True

ErrHandler:
    MsgBox ("An unforseen problem has occured. Please contact support.")
    Application.Cursor = xlDefault
    Application.ScreenUpdating = True
End Sub

我觉得我在重复我自己,而不是我应该重复的。在过程结束时和错误处理程序中编写这些类型的“标准”行,有没有更好的做法?

您只需让错误处理程序在代码的“清理”部分恢复,该部分在没有错误发生时也会执行:

   Clean_up:
        Application.Cursor = xlDefault
        Application.ScreenUpdating = True
        Exit Sub
    ErrHandler:
        MsgBox "An unforeseen problem has occurred. Please contact support."
        Resume Clean_up
    End Sub

如果在例程中始终包含该代码,则可以将其移动到从
清理
部分调用的单独例程。

您只需让错误处理程序在代码的“清理”部分恢复,该部分在没有错误发生时也会执行:

   Clean_up:
        Application.Cursor = xlDefault
        Application.ScreenUpdating = True
        Exit Sub
    ErrHandler:
        MsgBox "An unforeseen problem has occurred. Please contact support."
        Resume Clean_up
    End Sub

如果在例程中始终包含该代码,则可以将其移动到从
清理
部分调用的单独例程中。

简化此过程的一种方法是使用RAII模式:即编写一个在其类终止事件过程中进行清理的类。例如,您可以创建包含以下内容的类模块“CursorSaver”:

Private m_SavedCursor As XlMousePointer
Private m_SavedScreenUpdating As Boolean

Private Sub Class_Initialize()
    m_SavedCursor = Application.Cursor
    m_SavedScreenUpdating = Application.ScreenUpdating
End Sub

Private Sub Class_Terminate()
    Application.Cursor = m_SavedCursor
    Application.ScreenUpdating = m_SavedScreenUpdating
End Sub
然后,您可以在子类和函数的开始处创建该类的实例,当函数退出时,该类实例将超出范围,终止代码将自动调用并恢复初始状态:

Public Sub MySub
    Dim saver As New CursorSaver
    ...
    Exit Sub
End Sub ' Cursor and ScreenUpdating are automatically restored when the Sub exits

包含一个类似的示例。

简化此过程的一种方法是使用RAII模式:即编写一个类,在其class\u Terminate事件过程中执行清理。例如,您可以创建包含以下内容的类模块“CursorSaver”:

Private m_SavedCursor As XlMousePointer
Private m_SavedScreenUpdating As Boolean

Private Sub Class_Initialize()
    m_SavedCursor = Application.Cursor
    m_SavedScreenUpdating = Application.ScreenUpdating
End Sub

Private Sub Class_Terminate()
    Application.Cursor = m_SavedCursor
    Application.ScreenUpdating = m_SavedScreenUpdating
End Sub
然后,您可以在子类和函数的开始处创建该类的实例,当函数退出时,该类实例将超出范围,终止代码将自动调用并恢复初始状态:

Public Sub MySub
    Dim saver As New CursorSaver
    ...
    Exit Sub
End Sub ' Cursor and ScreenUpdating are automatically restored when the Sub exits

包含一个类似的示例。

我还发现自己在我的过程中反复添加类似的错误处理代码,至少可以说有点乏味。有一个名为VBIDE的加载项,它能够添加代码段,您可以轻松地将这些代码段嵌入到代码中,包括错误处理程序。这些代码段允许包含占位符,例如过程名称,因此它可以适应目标过程。早期版本是免费的,但v8确实需要花钱,我认为它有点贵,但它有很多其他功能,这是您的一个选择

这是一个示例错误处理程序,您可以看到占位符是如何工作的

Dim mpSettings As ApplicationSettings
Dim mpTopLevel As Boolean

    Const mpProcedure As String = "{PROCEDURE_NAME}"

    On Error GoTo {PROCEDURE_NAME}_Error
    'can be a top-level call or initiated by another
    mpTopLevel = IIf(IsArrayAllocated(mgVecProcStack), False, True)
    PushProcedureStack mpProcedure, mpTopLevel

    {PROCEDURE_NAME}= True

    Call AppSettings(State:="Set", _
                     AppType:=mpSettings, _
                     AppEvents:=True, _
                     AppScreen:=True, _
                     AppAlerts:=False, _
                     AppCalc:=appNotEnabled)

    {PROCEDURE_BODY}

{PROCEDURE_NAME}_Tidy:
    PopProcedureStack      

{PROCEDURE_NAME}_Tear_Down:               

{PROCEDURE_NAME}_Exit:
    Call AppSettings(State:="Reset", _
                     AppType:=mpSettings, _
                     AppEvents:=True, _
                     AppScreen:=True, _
                     AppAlerts:=False, _
                     AppCalc:=appNotEnabled)
    Exit Function

{PROCEDURE_NAME}_Error:
    If Err.Number = mgBypassErrorNum Then Resume {PROCEDURE_NAME}_Tear_Down
    {PROCEDURE_NAME} = False
    If AppErrorHandler(mmModule, mpProcedure, mpTopLevel) Then
        Stop
        Resume
    Else
        Resume {PROCEDURE_NAME}_Exit
    End If

我与MZTools或作者没有任何联系,它只是我使用的一个工具,并从中获得了价值。

我还发现自己在我的过程中反复添加类似的错误处理代码,至少可以说有点乏味。有一个名为VBIDE的加载项,它能够添加代码段,您可以轻松地将这些代码段嵌入到代码中,包括错误处理程序。这些代码段允许包含占位符,例如过程名称,因此它可以适应目标过程。早期版本是免费的,但v8确实需要花钱,我认为它有点贵,但它有很多其他功能,这是您的一个选择

这是一个示例错误处理程序,您可以看到占位符是如何工作的

Dim mpSettings As ApplicationSettings
Dim mpTopLevel As Boolean

    Const mpProcedure As String = "{PROCEDURE_NAME}"

    On Error GoTo {PROCEDURE_NAME}_Error
    'can be a top-level call or initiated by another
    mpTopLevel = IIf(IsArrayAllocated(mgVecProcStack), False, True)
    PushProcedureStack mpProcedure, mpTopLevel

    {PROCEDURE_NAME}= True

    Call AppSettings(State:="Set", _
                     AppType:=mpSettings, _
                     AppEvents:=True, _
                     AppScreen:=True, _
                     AppAlerts:=False, _
                     AppCalc:=appNotEnabled)

    {PROCEDURE_BODY}

{PROCEDURE_NAME}_Tidy:
    PopProcedureStack      

{PROCEDURE_NAME}_Tear_Down:               

{PROCEDURE_NAME}_Exit:
    Call AppSettings(State:="Reset", _
                     AppType:=mpSettings, _
                     AppEvents:=True, _
                     AppScreen:=True, _
                     AppAlerts:=False, _
                     AppCalc:=appNotEnabled)
    Exit Function

{PROCEDURE_NAME}_Error:
    If Err.Number = mgBypassErrorNum Then Resume {PROCEDURE_NAME}_Tear_Down
    {PROCEDURE_NAME} = False
    If AppErrorHandler(mmModule, mpProcedure, mpTopLevel) Then
        Stop
        Resume
    Else
        Resume {PROCEDURE_NAME}_Exit
    End If

我与MZTools或作者没有任何联系,它只是我使用的一个工具,并从中获得了价值。

如果你有一个专门的
ErrorHandler类或其他什么,这是可以的,但如果发现某个类是“静默”的,我会很不高兴更改应用程序状态。如果出于某种原因,我在子系统退出之前设置了saver=Nothing会怎么样?@RubberDuck-我不确定我是否理解您的担忧。该模式将是一个显式设计的类,用于在状态超出范围时保存/恢复状态:因此它不是“静默”的。如果您“设置saver=Nothing”,您将在子系统退出之前显式恢复已保存的状态。这正是我关心的问题。只要非常清楚这不是一种编码模式,而是一个专门处理它的类,就可以了。事实上,我觉得你的答案并不能说明这一点。@RubberDuck-RAII是一种比保存/恢复状态更适用的编码模式。例如,将VBA对Win32 API的调用封装在一个类中,可以简化清理,例如释放句柄。就像任何事情一样,它也可以被滥用,但我不确定是否需要明确指出。我不是说这种模式没有它的位置。我的观点是,对于这个用例,它是滥用的,对于任何未来的维护者来说都是一个巨大的WTF。我们只能同意不同意。有很多方法可以剥猫皮。如果你有一个专门的
ErrorHandler
类或其他什么的话,这很好,但是如果发现某个类“无声地”改变了应用程序的状态,我会很不高兴。如果出于某种原因,我在子系统退出之前设置了saver=Nothing会怎么样?@RubberDuck-我不确定我是否理解您的担忧。该模式将是一个显式设计的类,用于在状态超出范围时保存/恢复状态:因此它不是“静默”的。如果您“设置saver=Nothing”,您将在子系统退出之前显式恢复已保存的状态。这正是我关心的问题。只要非常清楚这不是一种编码模式,而是一个专门处理它的类,就可以了。事实上,我觉得你的答案并不能说明这一点。@RubberDuck-RAII是一种比保存/恢复状态更适用的编码模式。例如,将VBA对Win32 API的调用封装在一个类中,可以简化清理,例如释放句柄。就像任何事情一样,它也可以被滥用,但我不确定是否需要明确指出。我不是说这种模式没有它的位置。我的观点是,对于这个用例,它是滥用的,对于任何未来的维护者来说都是一个巨大的WTF。我们必须同意