我正在尝试了解如何针对下面引用的情况设置错误处理。 Sub A对其自己的上下文进行了错误处理。然而,它调用Sub B创建数据库连接,结果集等,它在错误处理程序中清理。它的方式,似乎只有ErrorHandler A在整个过程中都是活跃的。如何在调用B时激活ErrorHandler B,并在B返回后恢复为错误处理。
由于
Sub A
' Preps for database access
On Error GoTo ErrorHandlerA
B
.
.
Exit Sub
ErrorHandlerA:
...
Resume
End Sub
Sub B
' Does database access stuff
On Error ErrorHandlerB
cleanUp:
con.close
rs.close
Exit Sub
ErrorHandlerB:
GoTo cleanup
End Sub
答案 0 :(得分:4)
Exit Sub ErrorHandlerA: ... Resume End Sub
Resume
在这里,恢复正常执行(清除“错误处理模式”)并跳转到导致错误的指令。据推测(希望?)...
包含使之前失败的调用成功的代码。
cleanUp: con.close rs.close Exit Sub ErrorHandlerB: GoTo cleanup End Sub
这里你是GoTo
- 跳出错误处理子程序而不恢复正常执行,因此cleanUp
行标签下的指令执行错误 - 处理上下文。那应该是Resume cleanUp
,而不是GoTo
。
Exit Sub
语句离开作用域,清除错误状态并隐式恢复正常执行。这是一个小MCVE:
Public Sub Test()
On Error GoTo A
DoSomething
Debug.Print "After DoSomething: " & Err.Number
Exit Sub
A:
Debug.Print "Inside Test[A]: " & Err.Number
End Sub
Private Sub DoSomething()
On Error GoTo A
Err.Raise 5
B:
Debug.Print "Inside DoSomething[B]: " & Err.Number
Exit Sub
A:
Debug.Print "Inside DoSomething[A]: " & Err.Number
Resume B
End Sub
运行Test
过程会产生以下输出:
Inside DoSomething[A]: 5
Inside DoSomething[B]: 0
After DoSomething: 0
更改Resume B
的{{1}}指令会产生此输出:
GoTo B
正如您所看到的,由于错误是在Inside DoSomething[A]: 5
Inside DoSomething[B]: 5
After DoSomething: 0
中处理的,因此在执行返回DoSomething
时会重置错误状态,并且永远不会触发Test
过程中的处理程序。 / p>
如果要将错误传播给调用者,则有许多选项:
不处理被调用程序中的错误。在上面的示例中,这意味着Test
不会有DoSomething
语句;任何运行时错误都会“冒泡”调用堆栈,输出现在看起来像这样:
On Error
当调用代码知道最佳操作方法(例如向用户显示Inside Test[A]: 5
或记录错误等)时,通常会执行此操作。
处理错误,然后重新提升。在您的示例中,这意味着将清理代码移动(或复制)到MsgBox
子例程,并调用ErrorHandlerB
而不是Err.Raise Err.Number
。或者保留Resume
,然后清理子例程可以执行GoTo
以有效地重新抛出错误,以便调用者也可以处理。换句话说:
If Err.Number <> 0 Then Err.Raise Err.Number
输出:
Public Sub Test()
On Error GoTo A
DoSomething
Debug.Print "After DoSomething: " & Err.Number
Exit Sub
A:
Debug.Print "Inside Test[A]: " & Err.Number
End Sub
Private Sub DoSomething()
On Error GoTo A
Err.Raise 5
B:
Debug.Print "Inside DoSomething[B]: " & Err.Number
If Err.Number <> 0 Then Err.Raise Err.Number
Exit Sub
A:
Debug.Print "Inside DoSomething[A]: " & Err.Number
GoTo B 'resuming would clear the error and prevent rethrow
End Sub
根据您希望在两个不同位置处理相同错误的确切原因,您还可以考虑使Inside DoSomething[A]: 5
Inside DoSomething[B]: 5
Inside Test[A]: 5
成为B
,返回Function
表示成功或失败 - 在这种情况下Boolean
本身不再处理错误,而是使用正常的流量控制来确定要执行的操作:
A
产生此输出:
Public Sub Test()
On Error GoTo A
If Not DoSomething Then
Debug.Print "After DoSomething failed: " & Err.Number
Exit Sub
End If
Exit Sub
A:
Debug.Print "Inside Test[A]: " & Err.Number
End Sub
Private Function DoSomething() As Boolean
'Dim success As Boolean
On Error GoTo A
Err.Raise 5
'success = True
B:
Debug.Print "Inside DoSomething[B]: " & Err.Number
DoSomething = (Err.Number = 0) 'DoSomething = success
Exit Function
A:
Debug.Print "Inside DoSomething[A]: " & Err.Number
'success = False
GoTo B 'Resume B
End Function
注意Inside DoSomething[A]: 5
Inside DoSomething[B]: 5
After DoSomething failed: 0
知道Test
失败,但DoSomething
为0.这通常比使用运行时错误进行流量控制更可取,同样取决于您的实际情况。另请注意,在这种情况下,Err.Number
跳转是可以避免的,使用一个简单的布尔局部变量来跟踪您的返回值(注释掉的代码)。