处理数学函数中的错误
数学相关函数中错误处理的良好做法是什么?我正在构建一个专门函数的库(模块),我的主要目的是使调用这些函数的代码更容易调试——而不是创建一个闪亮的用户友好的错误处理工具。
下面是 VBA 中的一个简单示例,但我也有兴趣听到其他语言的信息。我不太确定应该在哪里返回错误消息/状态/标志。作为额外的论点?
Function AddArrays(arr1, arr2)
Dim i As Long
Dim result As Variant
' Some error trapping code here, e.g.
' - Are input arrays of same size?
' - Are input arrays numeric? (can't add strings, objects...)
' - Etc.
' 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
或类似以下内容。该函数返回一个布尔“成功”标志(如下例所示,如果输入数组不是数字等,则返回 False),或其他类型的错误号/消息。
Function AddArrays(arr1, arr2, result) As Boolean
' same code as above
AddArrays = booSuccess
End Function
不过我对此并不太着迷,因为它破坏了良好且可读的调用语法,即不能再说 c = AddArrays(a,b)
了。
我愿意接受建议!
What is good practice for error handling in math-related functions? I'm building up a library (module) of specialized functions and my main purpose is to make debugging easier for the code calling these functions -- not to make a shiny user-friendly error handling facility.
Below is a simple example in VBA, but I'm interested in hearing from other languages as well. I'm not quite sure where I should be returning an error message/status/flag. As an extra argument?
Function AddArrays(arr1, arr2)
Dim i As Long
Dim result As Variant
' Some error trapping code here, e.g.
' - Are input arrays of same size?
' - Are input arrays numeric? (can't add strings, objects...)
' - Etc.
' 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
or something like the following. The function returns a boolean "success" flag (as in the example below, which would return False if the input arrays weren't numeric etc.), or an error number/message of some other type.
Function AddArrays(arr1, arr2, result) As Boolean
' same code as above
AddArrays = booSuccess
End Function
However I'm not too crazy about this, since it ruins the nice and readable calling syntax, i.e. can't say c = AddArrays(a,b)
anymore.
I'm open to suggestions!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
显然,错误处理通常是一个大主题,最佳实践在很大程度上取决于您正在使用的语言的功能以及您正在编码的例程如何与其他例程相适应。因此,我将把我的答案限制为 VBA(在 Excel 中使用)和您所描述的那种库类型例程。
库例程中的异常与错误代码
在这种情况下,我不会使用返回代码。 VBA 支持一种异常处理形式,虽然不如 C++/Java/??.NET 中更标准的形式强大,但非常相似。因此,这些语言的建议通常适用。您可以使用异常来告诉调用例程,被调用例程由于某种原因无法完成其工作。您可以在最低级别处理异常,在这里您可以对失败做一些有意义的事情。
Bjarne Stroustrup 在本书中很好地解释了为什么在这种情况下异常比错误代码更好。 (这本书是关于 C++ 的,但 C++ 异常处理和 VBA 错误处理背后的原理是相同的。)
http://www2.research.att.com/~bs/3rd.html
这是第 8.3 节的精彩摘录:
第 14.1 节和第 14.9 节还讨论了库上下文中的异常与错误代码。 (archive.org 上有这本书的在线副本。)
在 stackoverflow 上可能有更多关于这方面的内容。我刚刚发现这个,例如:
异常与错误代码与错误代码断言
(可能存在涉及正确管理资源的陷阱,在使用异常时必须清除这些陷阱,但它们并不真正适用于此。)
VBA 中的异常
以下是引发异常的方法在 VBA 中查找(尽管 VBA 术语是“引发错误”):
如果此例程没有捕获错误,VBA 将为调用堆栈中位于其上方的其他例程提供机会(请参阅:VBA 错误“冒泡”)。调用者可能会这样做:
“做一些有意义的事情”的含义取决于应用程序的上下文。在上面的调用者示例中,it 决定应通过返回 Excel 可以放入工作表单元格中的错误值来处理某些错误,而其他错误则需要令人讨厌的警报。 (这里的 Excel 中的 VBA 的情况实际上是一个不错的具体示例,因为许多应用程序区分了内部例程和外部例程,以及您期望能够处理的异常和您只想了解的错误条件但你没有对此做出回应。)
不要忘记断言
因为你提到了调试,所以也值得注意断言的作用。如果您希望 AddArrays 只能由实际创建了自己的数组或以其他方式验证它们正在使用数组的例程调用,您可以这样做:
关于断言和异常之间差异的精彩讨论如下:
Debug.Assert vs 异常抛出
我在这里给出了一个例子:
断言是邪恶的吗?
关于通用数组处理例程的一些 VBA 建议
最后,作为 VBA 特定的请注意,VBA 变体和数组带有许多陷阱,当您尝试编写通用库例程时必须避免这些陷阱。数组可能有多个维度,它们的元素可能是对象或其他数组,它们的开始和结束索引可能是任何东西,等等。下面是一个示例(未经测试,并不试图详尽无遗),它解释了其中的一些内容:
Obviously error handling in general is a big topic, and what the best practice is depends a lot on the capabilities of the language you're working with and how the routine you're coding fits in with other routines. So I'll constrain my answer to VBA (used within Excel) and library-type routines of the sort you're describing.
Exceptions vs. Error Codes in Library Routines
In this case, I would not use a return code. VBA supports a form of exception handling that, while not as powerful as the more standard form found in C++/Java/??.NET, is pretty similar. So the advice from those languages generally applies. You use exceptions to tell calling routines that the called routine can't do it's job for whatever reason. You handle exceptions at the lowest level where you can do something meaningful about that failue.
Bjarne Stroustrup gives a very good explanation of why exceptions are better than error codes for this kind of situation in this book. (The book is about C++, but the principles behind C++ exception handling and VBA error handling are the same.)
http://www2.research.att.com/~bs/3rd.html
Here is a nice excerpt from Section 8.3:
Sections 14.1 and 14.9 also address exceptions vs. error codes in a library context. (There is a copy of the book online at archive.org.)
There is probably lots more about this on stackoverflow. I just found this, for example:
Exception vs. error-code vs. assert
(There can be pitfalls involving proper management of resources that must be cleaned up when using exceptions, but they don't really apply here.)
Exceptions in VBA
Here is how raising an exception looks in VBA (although the VBA terminology is "raising an error"):
If this routine doesn't catch the error, VBA will give other routines above it in the call stack a chance to (See this: VBA Error "Bubble Up"). Here is how a caller might do so:
What "do something meaningful" means depends on the context of your application. In the case of my caller example above, it decides that some errors should be handled by returning an error value that Excel can put in a worksheet cell, while others require a nasty alert. (Here's where the case of VBA within Excel is actually not a bad specific example, because lots of applications make a distinction between internal and external routines, and between exceptions you expect to be able to handle and error conditions that you just want to know about but for which you have no response.)
Don't Forget Assertions
Because you mentioned debugging, it's also worth noting the role of assertions. If you expect AddArrays to only ever be called by routines that have actually created their own arrays or otherwise verified they are using arrays, you might do this:
A fantastic discussion of the difference between assertions and exceptions is here:
Debug.Assert vs Exception Throwing
I gave an example here:
Is assert evil?
Some VBA Advice About General Array Handling Routines
Finally, as a VBA-specific note, there are VBA variants and arrays come with a number of pitfalls that must be avoided when you're trying to write general library routines. Arrays might have more than one dimension, their elements might be objects or other arrays, their start and end indices might be anything, etc. Here is an example (untested and not trying to be exhaustive) that accounts for some of that:
有很多方法可以捕获错误,有些方法比其他方法更好。这在很大程度上取决于错误的性质以及您想要如何处理它。
第一:在您的示例中,您没有处理基本的编译和操作。运行时错误(参见下面的代码)。
第二:使用上面的框架示例,您可以添加所有您想要的 if-then 逻辑错误捕获语句。将其送入 EH 步骤。如果您的函数足够复杂,您甚至可以添加多个 EH 步骤。通过这种方式设置,您可以找到发生逻辑错误的特定函数。
第三:在你的最后一个例子中,将该函数结束为布尔值并不是最好的方法。如果您能够将 2 个数组相加,那么该函数应该返回结果数组。如果没有,它应该抛出一个 msgbox 风格的错误。
第四:我最近开始做一个小技巧,在某些情况下非常有用。在 VBA 编辑器中,转到工具 -> 选项 -> 常规 -> 出现所有错误时中断。当您已经有了错误处理代码,但您想要转到发生错误的确切行并且不想删除完美的代码时,这非常有帮助。
示例:假设您想要捕获 VBA 通常无法捕获的错误,即整型变量的值应始终大于 2。在代码中的某个位置,输入
If intvar<=2 then goto EH
。然后在 EH 步骤中添加If intvar<=2 then msgbox "Intvar=" &整数。
There's lots of ways to trap errors, some better than others. Alot of it depends on on the nature of the error and how you want to handle it.
1st: In your examples, you aren't handling the basic compiling & runtime errors (see code below).
2nd: Using the framework example above, you can add all the if-then logical error trapping statements you want & feed it to the EH step. You can even add multiple EH steps if your function is complex enough. Setting things up this way allows you to find the particular function where your logic error occurred.
3rd: In your last example, ending that function as a boolean is not the best method. If you were able to add the 2 arrays, then that function should return the resultant array. If not, it should throw up a msgbox-style error.
4th: I recently started doing a little trick that can be very helpful in some situations. In your VBA Editor, go to Tools->Options->General->Break on ALL errors. This is very helpful when you already have your error handling code in place, but you want to go the exact line where the error occurred and you don't feel like deleting perfectly good code.
Example: Let's say you want to catch an error that wouldn't be caught normally by VBA, i.e. an integer variable should always have a value >2. Somewhere in your code, say
If intvar<=2 then goto EH
. Then in your EH step, addIf intvar<=2 then msgbox "Intvar=" & Intvar
.首先,PowerUser 已经给了你一个很好的答案——这是对该答案的扩展。
我刚刚学到的一个技巧是“双重恢复”,因此:
这里发生的情况是,在执行完成的代码时,您的代码在遇到错误时会抛出一个 MsgBox,然后运行 < code>Exit Function 语句 &继续前进(与使用
End Function
删除底部相同)。但是,当您进行调试并获得该 MsgBox 时,您可以手动执行 Ctrl-Break,然后将下一条语句 (Ctrl-F9) 设置为未修饰的Resume
并按 F8 继续执行 - 它会继续回到引发错误的行。您甚至不必删除额外的Resume
语句,因为如果没有手动干预,它们将永远执行。我想(温和地)与 PowerUser 争论的另一点是在最后一个例子中。我认为最好避免不必要的
GoTo
语句。更好的方法是If intvar<=2 then err.raise SomeCustomNumber
。确保您使用的号码尚未使用 - 搜索“VB 自定义错误”以获取更多信息。First, PowerUser gave you a good answer already--this is an expansion on that one.
A trick that I just learned is the "double resume", thus:
What happens here is that in the execution of finished code, your code throws up a MsgBox when an error is encountered, then runs the
Exit Function
statement & goes on its way (just the same as dropping out the bottom withEnd Function
). However, when you're debugging and you get that MsgBox you instead do a manual Ctrl-Break, then set next statement (Ctrl-F9) to the unadornedResume
and press F8 to step--it goes right back to the line that threw the error. You don't even have to take the extraResume
statements out, since they will never execute without manual intervention.The other point on which I want to argue (gently) with PowerUser is in the final example. I think it's best to avoid unneeded
GoTo
statements. A better approach isIf intvar<=2 then err.raise SomeCustomNumber
. Make sure you use a number that isn't already in use--search 'VB custom error' for more information.