【问题标题】:What is good practice for writing closing statements in the end of procedures and in error handlers?在过程结束和错误处理程序中编写结束语句的好习惯是什么?
【发布时间】:2014-12-25 03:20:53
【问题描述】:

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

    Application.Cursor = xlDefault
    Application.ScreenUpdating = True

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

我觉得我重复自己的次数超出了我的预期。在过程末尾和错误处理程序中编写这些“标准”行是否有更好的做法?

【问题讨论】:

标签: vba excel


【解决方案1】:

你可以简单地让你的错误处理程序在代码的“清理”部分恢复,当没有错误发生时也会执行:

   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 部分调用的单独例程中。

【讨论】:

  • + 1 我也更喜欢这个,而不是接受的答案中提到的Class,因为如果代码中断,Resume Clean_up 可以进行相关的清理。在针对Application.EnableEvents 之类的事件使用时非常有效!!!
【解决方案2】:

一种简化的方法是使用 RAII 模式:即编写一个在其 Class_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

This answer to another question 包含一个类似的例子。

【讨论】:

  • 如果你有一个专用的ErrorHandler 类或其他东西,这很好,但我会很沮丧地发现某些类正在“悄悄地”改变应用程序状态。如果出于某种原因我在子退出之前Set saver = Nothing 怎么办?
  • @RubberDuck - 不确定我是否理解您的担忧。这种模式将是一个明确设计用于在超出范围时保存/恢复状态的类:因此它不是“静默”的。如果您“Set saver = Nothing”,您将在子退出之前显式恢复保存状态。
  • 这正是我所关心的。只要完全清楚这不是编码模式,而是处理它的专用类,就可以了。事实上,我不觉得你的回答能说明这一点。
  • @RubberDuck - RAII 是一种编码模式,其适用性比仅仅保存/恢复状态更广泛。例如。在一个类中封装从 VBA 到 Win32 API 的调用使得释放句柄等清理工作变得更加简单。像任何事情一样,它可以被滥用,但我不确定是否需要明确指出。
  • 我并不是说这种模式没有它的位置。这只是我的观点,对于这个用例,它是滥用的,对于任何未来的维护者来说都是一个很大的WTF。我们将不得不同意不同意。给猫剥皮的方法很多。
【解决方案3】:

我还发现自己在我的程序中反复添加类似的错误处理代码,至少可以说有点乏味。有一个名为 MZTools 的 VBIDE 插件能够添加代码 sn-ps,您可以轻松地将其嵌入代码中,包括错误处理程序。 sn-ps 允许包含占位符,例如过程名称,因此它可以适应目标过程。早期版本是免费的,但 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或作者没有任何关系,它只是我使用并从中获得价值的工具。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-07-06
    • 1970-01-01
    • 2019-03-04
    • 2017-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-16
    相关资源
    最近更新 更多