【问题标题】:How to do an error handling block inline like Try/Catch如何像 Try/Catch 这样内联错误处理块
【发布时间】:2015-01-07 06:58:27
【问题描述】:

如何在 VBA 中执行内联错误处理例程?我不想把错误处理程序放在最后。

这是来自CPearson's Error Handling in VBA

Sub testErrHandling()
    On Error GoTo ErrHandler:

    Debug.print 9 / 0 'divide by zero error

    Worksheets("NewSheet").Activate 'missing worksheet error

    'more code here

    Exit Sub

ErrHandler:
    If Err.Number = 9 Then
        ' sheet does not exist, so create it
        Worksheets.Add.Name = "NewSheet"
        ' go back to the line of code that caused the problem
        Resume
    End If
End Sub

但我正在寻找更像 VB.net 中的 Try/Catch 块的东西

【问题讨论】:

  • VBA 比 VB.NET 古老得多,并且没有等效的方法来执行 try/catch 块。你可以用非常奇怪的语法和多个gotos fake 它,但我认为这样做没有任何意义..
  • @vba4all 您应该在下面查看我的答案。我为我的创新感到非常自豪。如果你愿意,你可以说它很奇怪,但历史往往会超越反对者。恕我直言,这当然不是坏代码。
  • 我明白你在那里做了什么,但对我来说,Resume EndTry1 与说GoTo <anyLabel> 和随机的gotos 没什么不同,都是坏习惯,应该不惜一切代价避免。顺便说一句,您在这里所做的been generally known for years already 可能没有在 SO 上提及
  • 你可以在 VBA 中模拟一个 TRY CTACH 块请看这里stackoverflow.com/q/30991653/4413676

标签: excel vba


【解决方案1】:

此代码将内联处理错误。这是用于处理错误的非常清晰的结构化模式。水流从上到下流动非常干净;这里没有意大利面条代码。

VBA 是一门古老的语言并且有局限性。使用错误处理的一种方法是使用Goto 语句,形式为On Error Goto <Label>Resume <Label>。这创造了一个机会。

传统上,错误处理程序放在底部。但是随着 VB.net 的进步,利用想法来改进代码似乎是合理的。 Try/Catch 是一种非常结构化的错误处理方式,并且非常容易遵循。这种模式试图以一种非常简洁的方式重现它。流程非常一致,不会从一个地方跳到另一个地方。

Sub InLineErrorHandling()

    'code without error handling

BeginTry1:

    'activate inline error handler
    On Error GoTo ErrHandler1

    'code block that may result in an error
    Dim a As String: a = "Abc"
    Dim c As Integer: c = a 'type mismatch

ErrHandler1:

    'handle the error
    If Err.Number <> 0 Then

        'the error handler is now active
        Debug.Print (Err.Description)

    End If

    'disable previous error handler (VERY IMPORTANT)
    On Error GoTo 0
    'exit the error handler
    Resume EndTry1

EndTry1:

    'more code with or without error handling

End Sub

来源:

如果管理得当,效果非常好。这是一种非常干净的流动模式,可以在任何需要的地方重现。

【讨论】:

  • 在真实环境中尝试一下 - 如果你明白我的意思 ;)
  • 我喜欢这个解决方案! Resume EndTry1 EndTry1: 有必要吗?我发现如果我删除它们,它的工作原理是一样的
  • @6diegodiego9 如果不使用 Resume,您将无法分配不同的错误处理程序...
  • 感谢 D_Bester!你是对的!经过深入研究,我仍然发现了一些错误/效率低下:Resume ... 必须移动到If Err.Number &lt;&gt; 0 分支的末尾(并且可以用On Error GoTo -1 将两行减少为一行),以提高效率和因为如果没有要处理的活动错误(即,如果“可能导致错误的代码块”不会导致错误),如果留在原始位置会导致崩溃。此外,注释“激活内联错误处理程序”应该是“启用内联错误处理程序”以使用正确的 MS 术语:)
  • 我发布了here我修改后的解决方案
【解决方案2】:

您可以尝试将您的对象分配到一个变量中,并改用 On Error Resume Next

Dim sh As Worksheet

'This is essentially the "Try" part
On Error Resume Next 'this ignores the error
Set sh = Worksheets("NewSheet")
On Error Goto 0 'this resets the active error handling routine

'Then this is the "Catch" part I guess
If sh Is Nothing Then 'check is something is assigned to sh
    'And I think this is "Finally" part
    Set sh = Worksheets.Add: sh.Name = "NewSheet" 'add otherwise
End If

对 Try/Catch 不太熟悉,因为我没有做过一些 VB.Net,但这是我能想到的最接近的内联纠错。 HTH。

【讨论】:

  • 这是一个很好的例子来说明如何解决这个特定的问题。但更广泛的问题是如何进行内联错误处理。
  • @D_Bester 这正是我提供的。 OERN + OEG0 实际上为您提供了Try 部分。之后,您实际上可以使用Select CaseIf(将用作您的Catch)来获取可能的错误编号并做出相应的反应。我同意 vba4all 关于Goto 的使用,所以我也尽可能避免它。我将自己限制为每个代码 1 个Goto :) 那就是我。这可能并非对所有人都是如此。
  • 优秀的代码我同意!这种方法可以解决很多问题。我将其称为 Try/Test 方法。它确实解决了内联错误。
【解决方案3】:

在对 VBA 错误处理的一些晦涩规则感到不安全/困惑数月后,对任何流行的文档页面(Microsoft VBA/VB、C. Pearson 等)都没有完全满意,我花了几天的时间反复试验重构
完整集(书面和未书面/更正的)管理 VBA 错误处理的规则:

LEGEND
1A:错误转到 0。1B:错误转到 行/标签1C:On Error Resume Next
2A:发生错误。 2B:错误转到 -1
3:暂时处于子程序中,直到回来
4:Erl
5:恢复 […]
6:退出 […],结束 […]

规则

  • 1A 生效*,错误处理将保持禁用不活动。这是默认设置。
  • 1B 生效*,错误处理最初是禁用不活动;它在 2A激活停用,而 35 6,而 32A6禁用
  • 1C 生效*,错误处理最初是禁用不活动;它保持不活动(假设在2A立即激活不活动),并且它禁用36
  • *= 如果 1A1B1C 在错误处理活动时被调用,则 Err 对象立即清除,但错误时操作更改效果会延迟,直到错误处理停用56 )。
  • 2B(瞬间)反转了2A的效果:错误处理inactivate并返回(1B)/stays(1C)启用。
  • 1A1B1C2B56 也会立即清除 Err 对象。
  • 如果在子过程中发生错误,如果在当前子过程中未处理(disabled),则将其传递给第一个调用(父)过程,在该过程中启用和不活动。如果没有找到,它会保留在那里(在子进程中)。
  • 4 如果错误处理是active,则返回最后一个错误的(如果指定为数字标签),否则返回0。
  • 如果在错误处理不活动时调用5(正确)崩溃(错误 21:“无错误恢复”)。

牢记所有这些规则,从此处发布的D_Bester's solution 开始,这是我修改后的解决方案,纠正了他的代码中的几个错误/效率低下:

    Sub InLineErrorHandling()
    
        'code without error handling

        On Error GoTo ErrHandler1  'enable error handler
        'code block that may result in an error
        Dim a As String: a = "Abc"
        Dim c As Integer: c = a 'type mismatch

        'inline error handler routine
    ErrHandler1:
        If Err.Number <> 0 Then
            Debug.Print err.Description
            On Error GoTo -1 ' inactivate error handler
        End If        
        On Error GoTo 0 'disable error handler
        
        'more code without error handling  (default mode)
        Err.Raise 123
    
    End Sub

...如果在错误处理例程中预计没有错误,可以进一步简化为这个(非常标准的)替代方案:

    Sub InLineErrorHandling()
    
        'code without error handling

        On Error Resume Next  'enable error handler        
        'code block that may result in an error
        Dim a As String: a = "Abc"
        Dim c As Integer: c = a 'type mismatch
    
        'inline error handler routine
        If Err.Number <> 0 Then
            Debug.Print err.Description
        End If
        On Error GoTo 0 'disable error handler
        
        'more code without error handling  (default mode)
        err.Raise 123
    
    End Sub

还值得一提的是,在both选项中,如果我们还想知道“可能导致错误的代码块”的哪一行(第一个为1B,最后一个为1C)导致错误,我们可以使用 Erl 函数,如下所示:

                 ...
            'code block that may result in an error
    10      Dim a As String: a = "Abc"
    20      Dim c As Integer: c = a 'type mismatch

            'inline error handler routine
            If Err.Number <> 0 Then
                Debug.Print "Error """ & err.Description & """ in line " & Err
                 ...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-21
    • 1970-01-01
    • 1970-01-01
    • 2017-09-14
    • 1970-01-01
    相关资源
    最近更新 更多