我完全厌倦了我一直在编写的所有错误代码,所以看起来我真的需要理解处理异常的正确方法。
让我们考虑以下示例:
interface IDataSource
{
string ReadData(int offset);
}
class FileDataSource : IDataSource {...}
class DatabaseDataSource : IDataSource {...}
如果我正在使用实现IDataSource的类对象,那么如果ReadData()由于某种原因失败,我可以期待捕获的异常是什么?
FileDataSource可能会失败,因为没有文件,或文件小于偏移量。 DatabaseDataSource可能会失败,因为它无法连接到数据库,或者该数据库中没有必需的表。
当我这样做时:
var data = dataSource.ReadData(10);
我应该抓到什么?从另一方面来说,如果我正在实施一个新的类FakeDataSource
,如果发生了不好的事情,我应该扔什么?
我有一种感觉,当我从FileNotFoundException
或FileDataSource
SqlException
抛出DatabaseDataSource
时,这意味着封装违规,因为我知道实施细节。
是否所有IDataSource
引发的异常都绑定到此接口?我的意思是 - 当我定义任何新的抽象时,我是否还应该定义相关的异常?像这样:
interface IDataSource { ... }
abstract class DataSourceException : Exception { ... }
因此,当有人决定实施IDataSource
时,他应该只抛出DataSourceException
。那是对的吗?那么offset
的错误值怎么样?我还应该抛出DataSourceException
s或标准(.NET)ArgumentOutOfRangeException
吗?
对于您认为值得的错误处理文章的任何链接也会表示赞赏。
答案 0 :(得分:3)
您应该只捕获可以处理的异常。如果您的IDataSource抛出FileNotFoundException或SqlException而您无法处理它,请不要捕获它。
抛出异常时,应抛出最能描述问题的异常类型。有three things that you want to know from a thrown exception。
- 出了什么问题?
- 哪里出错了?
- 为什么会出错?
当有效使用例外时,可以通过类型回答什么 抛出异常,由异常堆栈跟踪回答,和 为什么由异常消息回答。如果您发现了例外情况 没有回答所有三个问题,很可能他们没有 有效使用。三条规则将帮助您充分利用 调试程序时的异常。这些规则是:具体, 早点扔,迟到。
在大多数情况下,使用通常定义的例外而不是创建自己的例外。如果没有其他解释,人们将熟悉它们并理解它们的含义。但是,在需要封装特定模块的详细信息的情况下,创建自己的异常类型可能会有所帮助。采用这种方法时,将原始异常包装在模块的异常中,以便在重新抛出时不会丢失信息。
答案 1 :(得分:2)
只有在您的图书馆/应用程序无法执行要求执行操作的特殊情况下才会出现异常,并且需要帮助确定是否以及如何恢复。
您的例外情况应该确切地说明发生了什么。它们应该足够通用,以指示顶级问题是什么,其中的消息可以准确指出问题所在。根据不同类型的操作细分您的例外类别。
如果您在建立连接时遇到任何问题,请抛出ConnectionException,并使用该消息指定抛出异常的原因。如果无法连接到数据库,则消息应指示如此。用户名不正确?密码错误?让来电者知道。等等。
如果您的查询存在问题,如果您尝试查询的表不存在,则抛出QueryException。
将DataSource实现中的每个异常限制为DataSourceException几乎与将每个异常作为Exception类型抛出一样糟糕。你不会那样做,对吗?
我也不会在DataSourceException中包装异常。我认为这会给您的实现增加更多的复杂性。它还会使可读性变得更复杂,因为你会有一堆看起来像这样的代码:
new DataSourceException(new ArgumentException("Range is invalid"), "Range is invalid");
或者这个:
try {
...
} catch (Exception e) {
throw new DataSourceException(e, e.Message);
}
然后,对于所有使用您的实现的类,您必须捕获异常,然后查看内部异常以检查它是否是您想要优雅处理的内容。
这有意义吗?
答案 2 :(得分:2)
你为什么要抓住那个级别的任何东西?
你应该只抓住一个原因:你知道如何处理异常。
有人说如果你想记录就可以捕获,但在这种情况下你应该重新抛出异常。但这意味着在整个地方重复相同的日志行 - 更好的方法是在主循环中记录一次。该异常无论如何都包含堆栈跟踪。
捕获和重新抛出DataSourceException
是一种典型的Java解决方案,只有一个目标:满足Java编译器。毕竟,当您想要处理原因时更有用:FileNotFoundException
或DataSourceException
?
所以我的答案是:除非你知道如何处理异常,否则不要抓住!
答案 3 :(得分:1)
我认为没有任何针对抛出异常的具体规则。它可能是一个选择,对程序员来说非常主观。话虽这么说,你可以有不同的选择。就像你提到的那样,你可以抛出内置的异常,也可以从属于一个组的类中抛出自定义异常,比如实现接口。
在这种情况下,您可能最好抛出自己的自定义异常,因为您确切知道它来自何处以及如何处理它。 (没有真正的方法可以通过接口模型强制执行此操作)同样,它取决于当它冒泡时你正在做什么。对此没有严格的规则,但想法是你应该优雅地处理异常。我怀疑你是在编写有缺陷的代码。由于许多原因,例外情况发生。数据库连接失败不在您手中。在大多数情况下,数据库关闭时应用程序可能无法运行。除了捕获异常,记录,通知所需的组以及正常失败之外,您无法做很多事情。错误的应用程序将向用户显示服务器信息,而不会通知任何人,因此客户必须致电进行修复。
PS:我不相信从filedatasource(或来自sqlds的相应的一个)找不到抛出文件是违反封装的。该类应该知道实现细节,因为它是一个特定的目的类。
答案 4 :(得分:0)
你应该抛出任何描述发生的事情以及操作失败的原因。你应该只期望你真正可以处理的异常 - 永远不会捕获异常只是为了捕获它们。