In my opinion having the API throwing only one exception type is a bad idea. One of the good things with different exceptions is that you can choose to catch different types of exceptions and handle them differently. Wrapping up exceptions into a single exception type would remove that facility.
Use the exception types provided by the framework where appropriate, and create your own custom exception types for specific situations where appropriate. And above all, make sure to document for each method which exceptions they may throw.
When throwing exceptions, make sure that the user-facing exceptions are all relevant to what the user actually did wrong with regards to your toolkit. That might mean catching or merging different filesystem exceptions into a single exception but you shouldn't ever just catch Exception and throw a new one - that's not actually telling the toolkit user what they did wrong.
You should follow the same concepts as the .NET Framework. Your users already use the framework, so they know how it works and expect certain behavior.
Throw an ArgumentException-derived exception if an argument from the client is invalid (use ArgumentNullException, ArgumentOutOfRangeException, etc. as appropriate).
Throw an IOException-derived exception for IO errors.
Etc...
In your documentation, clearly state the preconditions for your public API and state the exceptions that will be thrown when they fail (like MSDN does).
我不喜欢它。如果将所有异常包装到 CustomException 中,则如果不在对象内部搜索 InnerExcpetion,则很难捕获特定异常(如果您的 API 还使用执行相同操作的其他 API,则可能是多个级别)。如果存在 OutOfMemoryException,我想知道,而不必去搜索它。
I don't like it. If you wrap all exceptions into a CustomException it will be difficult to catch specific exceptions without searching inside the object for the InnerExcpetion (possibly multiple levels deep if your API also uses other APIs that do the same thing). If there's an OutOfMemoryException, I'd like to know that without having to go searching for it.
You need the different exception types to be able to separate one case from another. For anything that you'd let it flow up until a generic exception handler block catches it would seem k, but you are preventing any other code recovery - extra actions that operate on specific cases.
You can wrap a set of exceptions, if those fit into a specific scenario that can be handled by the calling code. Don't forget that some framework exceptions already communicate the situation well.
弗雷德里克·莫克所说的非常正确。我还想补充一点,您可以轻松地记录使用 xml 注释(和 GhostDoc)引发的异常。
因此,API 用户可以查找文档以了解可以引发哪些异常。然而,有一个缺点。
如果你的 API 中有一个很深的调用栈,特别是如果圈复杂度变高(即可能的分支数量),你无法恢复所有可能抛出的异常(可能是运行时?)
所以我建议仅在某些情况下抛出如果无法恢复,则仅将其放入 API 的上层,即深度不超过 2。并捕获在调用堆栈中更深处抛出的所有异常,并将它们作为 InnerException 或类似的东西返回。
What Fredrik Mork says is very true. I also like to add to that that you can easily document the exceptions that are thrown with xml comments (and GhostDoc).
So an API user can lookup in the documentation to see what exceptions can be thrown. However, there is a downside.
If you have a deep callstack inside your API and especially if the cyclomatic complexity becomes high (i.e. num of possible branches) you cannot recover all possible exceptions that can be thrown (perhaps by the runtime?)
So i would recommend to only throw if something if recovery is not possible and only throw in the upper layer of your API, i.e. no deeper than 2 deep. And catch all exceptions that are thrown deeper in your callstack and return them as InnerException or something like that.
通常,将 IO 类型的异常不加修改地传递到调用堆栈是有意义的,因为无论如何您都无能为力。如果您的 API 在 GUI 级别运行(这将是相当不寻常的),则例外情况是您可能会重试的预期错误,或者询问用户要做什么。
请务必记录您的 API 方法可以抛出哪些此类异常。
对于第二种类型(始终可以防止的异常),这些异常可能是由 API 在错误输入时生成的,但永远不应该从较低级别向上传递到调用堆栈。您调用的任何内容的输入都应该经过验证,以便这些错误不会发生,并且如果由于某种原因可能发生这些错误,那么这些异常应该被包装起来。否则,使用 API 的代码将不得不在错误的抽象级别处理异常。
再次强调,一定要记录什么样的输入不会产生异常,以及如果违反这些规则会出现什么样的异常。
在所有情况下,抛出对 API 用户有意义的异常,而不是对 API 本身编程的人有意义的异常。
There are two types of exceptions to consider (ignoring StackOverflowException, etc., which you can't do anything about anyway):
Those that are caused by external issues (like file IO), which have to be handled.
Those that can always be avoided with properly validated input.
It usually makes sense for the IO-type exceptions to be passed up the call stack unmodified, since there isn't anything you can really do about it anyway. Exceptions to this would be anticipated errors for which you might do retries, or asking the user what to do, if your API operates at the GUI level (which would be rather unusual).
Just be sure to document which exceptions of this type your API methods can throw.
Of the second type (exceptions that can always be prevented), these exceptions may be generated by your API on bad input, but should never be passed up the call-stack from the lower levels. Input to anything you call should be validated so that these errors do not occur, and if, for some reason, they can occur, then these exceptions should be wrapped up. Otherwise, the code that uses your API will have to deal with exceptions at the wrong level of abstraction.
And once again, be sure to document what sort of input will not generate exceptions, and what kinds of exceptions to expect if these rules are broken.
In all cases, throw exceptions that make sense to the user of your API, not the person programming the API itself.
对于您的异常策略,我首先会问:“如果我不知道这个 API 实际与什么对象通信,我会期望返回什么样的异常?”请记住,方法是对对象执行某些操作的请求,而异常是对象让您知道它不能执行操作以及原因的方式。
The problem with catching System.Exception in the general case is that by doing so, you're effectively saying "I don't have any idea of why this failed, and that's okay, because I know I can continue anyway!"
That's rarely, if ever, true.
Wrapping up all exceptions in a single custom exception type means that your consumers will just catch that type - which is then the moral equivalent of catching System.Exception. While you may satisfy some policy/FXCop rule by doing that, you're really still just catching System.Exception.
For your exception policy, I'd start with asking this: "If I didn't know what this API actually talked to, what kind of exception would I expect to get back?" And remember that a method is a request to an object to do something, and an exception is the object's way of letting you know that it couldn't, and why.
If you create a single exception type for use throughout your API, its meaning will be vague to client applications that encounter that exception type. Instead, consider creating an exception class hierarchy for your API.
To do so, first define an abstract base exception type:
CustomException
...and then derive more specific exception types from it. For example:
The abstract base type CustomException will never be directly thrown from your API. However, a client of your API can catch the base exception type CustomException if it doesn't care about which specific derived exception was thrown from your API.
发布评论
评论(9)
在我看来,让 API 只抛出一种异常类型是一个坏主意。不同异常的好处之一是您可以选择捕获不同类型的异常并以不同的方式处理它们。将异常包装到单个异常类型中会删除该功能。
在适当的情况下使用框架提供的异常类型,并在适当的情况下为特定情况创建您自己的自定义异常类型。最重要的是,确保记录每个方法可能抛出的异常。
In my opinion having the API throwing only one exception type is a bad idea. One of the good things with different exceptions is that you can choose to catch different types of exceptions and handle them differently. Wrapping up exceptions into a single exception type would remove that facility.
Use the exception types provided by the framework where appropriate, and create your own custom exception types for specific situations where appropriate. And above all, make sure to document for each method which exceptions they may throw.
引发异常时,请确保面向用户的异常都与用户在工具包中实际犯的错误相关。这可能意味着捕获不同的文件系统异常或将其合并到一个异常中,但您不应该只是捕获异常并抛出一个新异常 - 这实际上并没有告诉工具包用户他们做错了什么。
When throwing exceptions, make sure that the user-facing exceptions are all relevant to what the user actually did wrong with regards to your toolkit. That might mean catching or merging different filesystem exceptions into a single exception but you shouldn't ever just catch Exception and throw a new one - that's not actually telling the toolkit user what they did wrong.
您应该遵循与 .NET Framework 相同的概念。您的用户已经使用该框架,因此他们知道它是如何工作的并期望某些行为。
ArgumentException
派生的异常(根据需要使用ArgumentNullException
、ArgumentOutOfRangeException
等)。等等...
在您的文档中,明确说明公共 API 的先决条件,并说明失败时将引发的异常(如 MSDN 那样)。
You should follow the same concepts as the .NET Framework. Your users already use the framework, so they know how it works and expect certain behavior.
ArgumentException
-derived exception if an argument from the client is invalid (useArgumentNullException
,ArgumentOutOfRangeException
, etc. as appropriate).IOException
-derived exception for IO errors.Etc...
In your documentation, clearly state the preconditions for your public API and state the exceptions that will be thrown when they fail (like MSDN does).
我不喜欢它。如果将所有异常包装到 CustomException 中,则如果不在对象内部搜索 InnerExcpetion,则很难捕获特定异常(如果您的 API 还使用执行相同操作的其他 API,则可能是多个级别)。如果存在 OutOfMemoryException,我想知道,而不必去搜索它。
I don't like it. If you wrap all exceptions into a CustomException it will be difficult to catch specific exceptions without searching inside the object for the InnerExcpetion (possibly multiple levels deep if your API also uses other APIs that do the same thing). If there's an OutOfMemoryException, I'd like to know that without having to go searching for it.
不要对所有人使用相同的异常。
您需要不同的异常类型才能将一种情况与另一种情况分开。对于任何你让它流动直到通用异常处理程序块捕获它似乎是 k,但你正在阻止任何其他代码恢复 - 在特定情况下运行的额外操作。
您可以包装一组异常,如果这些异常适合调用代码可以处理的特定场景。不要忘记一些框架异常已经很好地传达了情况。
Don't use the same exception for all.
You need the different exception types to be able to separate one case from another. For anything that you'd let it flow up until a generic exception handler block catches it would seem k, but you are preventing any other code recovery - extra actions that operate on specific cases.
You can wrap a set of exceptions, if those fit into a specific scenario that can be handled by the calling code. Don't forget that some framework exceptions already communicate the situation well.
弗雷德里克·莫克所说的非常正确。我还想补充一点,您可以轻松地记录使用 xml 注释(和 GhostDoc)引发的异常。
因此,API 用户可以查找文档以了解可以引发哪些异常。然而,有一个缺点。
如果你的 API 中有一个很深的调用栈,特别是如果圈复杂度变高(即可能的分支数量),你无法恢复所有可能抛出的异常(可能是运行时?)
所以我建议仅在某些情况下抛出如果无法恢复,则仅将其放入 API 的上层,即深度不超过 2。并捕获在调用堆栈中更深处抛出的所有异常,并将它们作为 InnerException 或类似的东西返回。
What Fredrik Mork says is very true. I also like to add to that that you can easily document the exceptions that are thrown with xml comments (and GhostDoc).
So an API user can lookup in the documentation to see what exceptions can be thrown. However, there is a downside.
If you have a deep callstack inside your API and especially if the cyclomatic complexity becomes high (i.e. num of possible branches) you cannot recover all possible exceptions that can be thrown (perhaps by the runtime?)
So i would recommend to only throw if something if recovery is not possible and only throw in the upper layer of your API, i.e. no deeper than 2 deep. And catch all exceptions that are thrown deeper in your callstack and return them as InnerException or something like that.
有两种类型的异常需要考虑(忽略 StackOverflowException 等,无论如何你都无能为力):
通常,将 IO 类型的异常不加修改地传递到调用堆栈是有意义的,因为无论如何您都无能为力。如果您的 API 在 GUI 级别运行(这将是相当不寻常的),则例外情况是您可能会重试的预期错误,或者询问用户要做什么。
请务必记录您的 API 方法可以抛出哪些此类异常。
对于第二种类型(始终可以防止的异常),这些异常可能是由 API 在错误输入时生成的,但永远不应该从较低级别向上传递到调用堆栈。您调用的任何内容的输入都应该经过验证,以便这些错误不会发生,并且如果由于某种原因可能发生这些错误,那么这些异常应该被包装起来。否则,使用 API 的代码将不得不在错误的抽象级别处理异常。
再次强调,一定要记录什么样的输入不会产生异常,以及如果违反这些规则会出现什么样的异常。
在所有情况下,抛出对 API 用户有意义的异常,而不是对 API 本身编程的人有意义的异常。
There are two types of exceptions to consider (ignoring StackOverflowException, etc., which you can't do anything about anyway):
It usually makes sense for the IO-type exceptions to be passed up the call stack unmodified, since there isn't anything you can really do about it anyway. Exceptions to this would be anticipated errors for which you might do retries, or asking the user what to do, if your API operates at the GUI level (which would be rather unusual).
Just be sure to document which exceptions of this type your API methods can throw.
Of the second type (exceptions that can always be prevented), these exceptions may be generated by your API on bad input, but should never be passed up the call-stack from the lower levels. Input to anything you call should be validated so that these errors do not occur, and if, for some reason, they can occur, then these exceptions should be wrapped up. Otherwise, the code that uses your API will have to deal with exceptions at the wrong level of abstraction.
And once again, be sure to document what sort of input will not generate exceptions, and what kinds of exceptions to expect if these rules are broken.
In all cases, throw exceptions that make sense to the user of your API, not the person programming the API itself.
在一般情况下捕获 System.Exception 的问题在于,通过这样做,您实际上是在说“我不知道为什么会失败,但这没关系,因为我知道无论如何我都可以继续!”
这种情况很少(如果有的话)是真的。
将所有异常包装在单个自定义异常类型中意味着您的使用者将只捕获该类型 - 这在道德上相当于捕获 System.Exception。虽然您可以通过这样做来满足某些策略/FXCop 规则,但您实际上仍然只是捕获 System.Exception。
对于您的异常策略,我首先会问:“如果我不知道这个 API 实际与什么对象通信,我会期望返回什么样的异常?”请记住,方法是对对象执行某些操作的请求,而异常是对象让您知道它不能执行操作以及原因的方式。
The problem with catching System.Exception in the general case is that by doing so, you're effectively saying "I don't have any idea of why this failed, and that's okay, because I know I can continue anyway!"
That's rarely, if ever, true.
Wrapping up all exceptions in a single custom exception type means that your consumers will just catch that type - which is then the moral equivalent of catching System.Exception. While you may satisfy some policy/FXCop rule by doing that, you're really still just catching System.Exception.
For your exception policy, I'd start with asking this: "If I didn't know what this API actually talked to, what kind of exception would I expect to get back?" And remember that a method is a request to an object to do something, and an exception is the object's way of letting you know that it couldn't, and why.
如果您创建在整个 API 中使用的单个异常类型,则其含义对于遇到该异常类型的客户端应用程序来说将是模糊的。相反,请考虑为您的 API 创建异常类层次结构。
为此,首先定义一个抽象的基本异常类型:
...然后从中派生出更具体的异常类型。例如:
抽象基类型
CustomException
永远不会直接从 API 中抛出。但是,如果 API 客户端不关心从 API 引发的特定派生异常,则它可以捕获基本异常类型CustomException
。有关开发异常类层次结构的更多信息,请参阅此答案以及此问题的其他答案:为什么在 .NET 中创建自定义异常?
另请参阅这个答案介绍了使异常层次结构的基类抽象的想法。
If you create a single exception type for use throughout your API, its meaning will be vague to client applications that encounter that exception type. Instead, consider creating an exception class hierarchy for your API.
To do so, first define an abstract base exception type:
...and then derive more specific exception types from it. For example:
The abstract base type
CustomException
will never be directly thrown from your API. However, a client of your API can catch the base exception typeCustomException
if it doesn't care about which specific derived exception was thrown from your API.For more on the idea of developing an exception class hierarchy, see this answer and other answers to this question: Why create custom exceptions in .NET?
Also see this answer which introduces the idea of making the base class of your exception hierarchy abstract.