12

Sometimes a certain bit of code will raise an error in an expected way, and it's most convenient to handle it locally rather than throw it to an error handling routine where it will get mixed up with other errors of the same type. Yet you don't want unexpected errors to be swallowed; you want them to be raised as usual.

In the (slightly contrived) example below, the FindInArray function can raise different types of error. One of them, ERR__ELEMENT_NOT_FOUND_IN_ARRAY, is more or less expected and so I want to handle it locally. But other error numbers may also occur, and if so I want them to be dealt with by the error handling routine.

I find that if I deal with some expected error numbers locally, I can't easily "rethrow" unexpected error numbers to be dealt with elsewhere.

How do I segregate the expected errors I want to deal with locally, from unexpected errors to be dealt with in error handling routine (or elsewhere)?

On Error GoTo ErrorHandler

'Some code...

'Here I want to trap a likely/expected error locally, because the same
'error may occur elsewhere in the procedure but require different handling.
On Error Resume Next
personIndex = FindInArray(personName, personArray)
If Err.Number = ERR__ELEMENT_NOT_FOUND_IN_ARRAY Then
    MsgBox "Name not found in person array. Using default person."
Else
    'What if it's a different kind of error?
    ' .e.g. ERR__ARRAY_CONTAINS_TWO_PERSONS_WITH_SAME_NAME
    'I want to rethrow it, but can't because On Error Resume Next swallows it.
End If
On Error GoTo ErrorHandler 'back to normal
'I can't rethrow it here either, because On Error Goto cleared the Err object.

'-----------------------
ErrorHandler:
Select Case Err.Number
Case ERR__ELEMENT_NOT_FOUND_IN_ARRAY
    'The error number doesn't give me enough info 
    'to know what to do with it here!
Case ERR__ARRAY_CONTAINS_TWO_PERSONS_WITH_SAME_NAME
    'Existing code to deal with this error
Case ...

I guess I could "save" the error Number, Source, Description, etc. in some other variable / object, and use those to raise an error after On Error GoTo ErrorHandler 'back to normal, (and in fact I have implemented this just to see) but that seems terribly inconvenient and clumsy.

4

4 回答 4

3

我创建了一个用户定义的类型,它具有与对象相同的成员Err(编号、来源、描述等)。该SaveErr函数基本上会将对象属性的值复制Err到这种类型的变量中,并RaiseSavedErr使用这些属性值引发错误。

当然,可以使用类和方法而不是用户定义的类型和函数/子类来完成完全相同的事情。但想法是一样的。

例子:

    On Error Resume Next
    personIndex = FindInArray(personName, personArray)
    savedErr = SaveErr(Err) 'Save values of Number, Source, Description, etc.
    On Error GoTo ErrorHandler
    'Segregate error handling strategies here using savedErr
    If savedErr.Number = ERR__ELEMENT_NOT_FOUND_IN_ARRAY Then
        MsgBox "Name not found in person array. Using default person."
    Else
        RaiseSavedErr savedErr 'rethrows the error
    End If

我想知道是否有更标准或更优雅的方式来做到这一点。

于 2013-10-30T12:25:01.110 回答
3

尽管我对所提出的问题感到有些困惑(到目前为止,我已经阅读了很多次:-)),但我有一种非常强烈的感觉,即这种困境的根源在于函数范围。
如果没问题,我将使用一些基本示例来显示模式但与您的代码不是 1-1 相提并论。

如何将要在本地处理的预期错误与要在错误处理例程(或其他地方)中处理的意外错误分开?

我觉得答案就在问题本身之内。
错误处理程序在您从较低级别的子例程或函数调用的子例程/函数的本地范围内起作用。

我发现如果我在本地处理一些预期的错误号,我不能轻易地“重新抛出”意外的错误号以在其他地方处理。

如果您将要检查本地错误的代码委托给放置在call stack. 由于它们在自己的范围内处理错误,因此它们不会相互混淆。

考虑这段代码:

Sub baseSub()

    Dim n As Integer

    n = checkDivision(1, 0)      
    n = 1 / 0  ' cause an error

End Sub

Public Function checkDivision(iNumerator As Integer, iDenominator As Integer)

    On Error Resume Next
    checkDivision = iNumerator / iDenominator

    If Err.Number <> 0 Then
        checkDivision = Err.Number
        Exit Function
    End If

End Function

相反:当On Error Resume NextbaseSub应用时,所有位于调用堆栈顶部的函数也将忽略错误。但是,反过来也行不通。

我想你可以利用这个来发挥你的优势。

因此,总而言之,我相信您可以通过在您放置在调用堆栈更高级别的委托函数中捕获预期错误来解决问题。

如果这不起作用,那么我没有想法。

于 2013-12-11T10:06:54.020 回答
3

这个答案是我对手头问题的看法,也许从稍微不同的角度来看。

在考虑这个代码块时:

On Error Resume Next
personIndex = FindInArray(personName, personArray)
If Err.Number = ERR__ELEMENT_NOT_FOUND_IN_ARRAY Then
    MsgBox "Name not found in person array. Using default person."
Else
End If

您在标题中提到:“预期错误”。
但问题是,如果您事先知道它可能会发生,就不应该抛出错误。
在我看来,它们是一种形式validation,应该以条件语句的形式内置到函数中。

前面提到的代码块在基本层面上是这样的:

    If Not (in_array(vArray, "Jean-Francois")) Then
        MsgBox "Name not found in person array. Using default person."
    End If

在我看来,这更清洁和可读。
使用不属于基本代码的自定义函数,但可以在幕后进行检查。可重用函数可以包装在您使用的模块中,其使用方式与静态类非常相似。

Public Function in_array(vArray As Variant, sItem As String) As Boolean

    Dim lCnt As Long

    in_array = False
    Do Until lCnt = UBound(vArray) + 1
        If StrComp(vArray(lCnt), sItem, CompareMethod.Text) = 0 Then
            in_array = True
            Exit Function
        End If
        lCnt = lCnt + 1
    Loop

End Function

更好的是在in_array()函数内部使用该函数,findInArray()并且在 basesub 中只有 1 行代码,即:

personIndex = FindInArray(personName, personArray)

让后面的函数处理剩下的,拦截你能预见的异常。
这只是一个示例,显然您编写了对您有用的函数和返回值,您可能会添加更广泛的验证。

我的观点是,这些返回值是作为应用程序/验证逻辑一部分的返回消息,我不认为它们是技术错误 - 因此,我认为将错误处理程序作为自定义使用没有任何好处created 函数在(我认为)更清晰的结构中完全符合您的需求。

当您将例如三个参数传递给函数调用而它只接受两个时,我认为这是一个技术错误。错误处理程序会通知您,之后开发人员可能会决定通过允许例如使当前函数更具动态性。可选参数并修复错误。

于 2013-12-10T15:46:04.340 回答
2

On Error Resume Next是 VBA 中万恶之源;)

我还没有看到您的整个代码,但是您在问题中提出的问题可以通过使用MULTIPLE ERROR HANDLERS & RESUME轻松解决。它比创建自定义 Err 对象和引发错误事件要简单得多......

Public Sub sixsixsixBytes()
    On Error GoTo COMMON_ERROR_HANDLER
   'Some code...

    On Error GoTo ARRAY_ERROR_HANDLER
    Call Err.Raise(123)  'lets say error occured in personIndex = ....
    'it will jump to 2nd error handler and come back
    'some code again... 
    'If statement is not required at all
    Call Err.Raise(666)

    On Error GoTo COMMON_ERROR_HANDLER:
    'some code again...
    Call Err.Raise(234)
    Exit Sub

'# MULTIPLE ERROR HANDLERS
COMMON_ERROR_HANDLER:
    Select Case Err.Number
           Case 234: MsgBox 234
           Case 345: MsgBox 345
    End Select

ARRAY_ERROR_HANDLER:
    Select Case Err.Number
           Case 123:
                MsgBox "Name not found in person array. Using default person."
                Resume Next 'or Resume after changing a value (as per your need)
           Case 666:
                MsgBox "Some other error"
                Resume Next
    End Select
End Sub
于 2013-12-09T20:18:09.340 回答