很明显,错误处理一般是一个大问题,最佳实践取决于您正在使用的语言的功能以及您编写的例程如何与其他例程匹配。所以我会将我的答案限制在VBA(在Excel中使用)和您所描述类型的库类型例程中。
异常与错误代码库例程
在这种情况下,我会不使用返回代码。 VBA支持一种异常处理形式,虽然不如C++/Java/?? .NET中更标准的形式那么强大,但是它非常相似。所以这些语言的建议通常适用。您使用异常来告诉调用例程,无论出于何种原因,被调用例程都无法完成它的工作。您可以在最低级别处理异常,您可以对该失败做一些有意义的事情。
Bjarne Stroustrup给出了一个非常好的解释,说明为什么异常比这本书中的这种情况的错误代码更好。 (这本书是关于C++,但背后的C++异常处理和VBA错误处理的原理是一样的。)
http://www2.research.att.com/~bs/3rd.html
下面是8.3节一个很好的摘录:
当一个程序由单独的 模块组成,尤其是当这些模块来自单独开发的 库时,错误处理需要被分成两个不同部分的 :[1] 报告错误条件 无法在本地解决[2] 在其他地方检测到的错误处理 库的作者可以检测到 运行时错误,但通常 不知道如何处理它们。 图书馆的用户可能知道如何处理这种错误,但不能 检测到它们 - 否则它们会在用户的代码中处理 ,而不是 ,以供库找到。
第14.1和14.9节还讨论了库上下文中的异常与错误代码。 (在archive.org上有一本在线书的副本。)
关于此问题,可能还有更多关于此问题的信息。我刚刚发现这一点,例如:
Exception vs. error-code vs. assert
(有可能是一个涉及资源的适当管理陷阱必须使用异常时进行清理,但他们并不真正适用于此)
异常在VBA
这里是如何引发异常看起来VBA(虽然VBA术语“引发错误”):
Function AddArrays(arr1, arr2)
Dim i As Long
Dim result As Variant
' Some error finding code here, e.g.
' - Are input arrays of same size?
' - Are input arrays numeric? (can't add strings, objects...)
' - Etc.
'Assume errorsFound is a variable you populated above...
If errorsFound Then
Call Err.Raise(SOME_BAD_INPUT_CONSTANT) 'See help about the VBA Err object. (SOME_BAD_INPUT_CONSTANT is something you would have defined.)
End If
' If no errors found, do the actual work...
ReDim result(LBound(arr1) To UBound(arr1))
For i = LBound(arr1) To UBound(arr1)
result(i) = arr1(i) + arr2(i)
Next i
AddArrays = result
End Function
如果这个例程没有发现错误,VBA会在调用堆栈中给它上面的其他例程一个机会(参见:VBA Error "Bubble Up")。以下是主叫方如何做到这一点:
Public Function addExcelArrays(a1, a2)
On Error Goto EH
addExcelArrays = AddArrays(a1, a2)
Exit Function
EH:
'ERR_VBA_TYPE_MISMATCH isn't defined by VBA, but it's value is 13...
If Err.Number = SOME_BAD_INPUT_CONSTANT Or Err.Number = ERR_VBA_TYPE_MISMATCH Then
'We expected this might happen every so often...
addExcelArrays = CVErr(xlErrValue)
Else
'We don't know what happened...
Call debugAlertUnexpectedError() 'This is something you would have defined
End If
End Function
什么“做有意义的事情”意味着取决于您的应用程序的上下文。在上面我的调用者示例中,它决定应通过返回Excel可放入工作表单元格中的错误值来处理某些错误,而其他错误值需要令人讨厌的警报。 (这里的Excel中VBA的情况实际上并不是一个不好的具体例子,因为许多应用程序会区分内部和外部例程,以及您希望能够处理的异常和您只想了解的错误条件但你都没有反应。)
不要忘记断言
因为你提到的调试,这也是值得注意的断言的作用。如果你希望AddArrays到永远只能由实际上已经创建了自己的阵列或以其他方式证实他们正在使用的阵列程序被调用,你可以这样做:
Function AddArrays(arr1, arr2)
Dim i As Long
Dim result As Variant
Debug.Assert IsArray(arr1)
Debug.Assert IsArray(arr2)
'rest of code...
End Function
断言和异常之间的区别的一个梦幻般的讨论是在这里:
Debug.Assert vs Exception Throwing
我给这里一个例子:
Is assert evil?
一些VBA咨询关于一般阵列处理例程
最后,作为一个特定的VBA音符,有VBA变种和数组来了一些陷阱必须当你试图写一般的库来避免例程。数组可能有多个维度,它们的元素可能是对象或其他数组,它们的开始和结束索引可能是任何东西等等。下面是一个示例(未经测试,并且没有试图详尽无遗),其中包含以下内容:
'NOTE: This has not been tested and isn't necessarily exhaustive! It's just
'an example!
Function addArrays(arr1, arr2)
'Note use of some other library functions you might have...
'* isVect(v) returns True only if v is an array of one and only one
' dimension
'* lengthOfArr(v) returns the size of an array in the first dimension
'* check(condition, errNum) raises an error with Err.Number = errNum if
' condition is False
'Assert stuff that you assume your caller (which is part of your
'application) has already done - i.e. you assume the caller created
'the inputs, or has already dealt with grossly-malformed inputs
Debug.Assert isVect(arr1)
Debug.Assert isVect(arr2)
Debug.Assert lengthOfArr(arr1) = lengthOfArr(arr2)
Debug.Assert lengthOfArr(arr1) > 0
'Account for VBA array index flexibility hell...
ReDim result(1 To lengthOfArr(arr1)) As Double
Dim indResult As Long
Dim ind1 As Long
ind1 = LBound(arr1)
Dim ind2 As Long
ind2 = LBound(arr2)
Dim v1
Dim v2
For indResult = 1 To lengthOfArr(arr1)
'Note implicit coercion of ranges to values. Note that VBA will raise
'an error if an object with no default property is assigned to a
'variant.
v1 = arr1(ind1)
v2 = arr2(ind2)
'Raise errors if we have any non-numbers. (Don't count a string
'with numeric text as a number).
Call check(IsNumeric(v1) And VarType(v1) <> vbString, xlErrValue)
Call check(IsNumeric(v2) And VarType(v2) <> vbString, xlErrValue)
'Now we don't expect this to raise errors.
result(indResult) = v1 + v2
ind1 = ind1 + 1
ind2 = ind2 + 1
Next indResult
addArrays = result
End Function
哇。如果可以的话,我会多次投票。 – RolandTumble 2010-12-13 20:03:40
谢谢。在VBA中我有一个关于EH的屁股,因为人们总是唠叨不休。而且他们几乎总是将它与具有非常相似的语言(虽然不太笨重,并且承认不具有诸如On Error Resume Next之类的)异常处理形式的语言进行比较。 – jtolle 2010-12-14 00:37:33
这是一个屁股答案!谢谢。 – 2010-12-15 09:04:36