我应该抛出/期望抛出什么?

时间:2011-07-23 18:03:23

标签: c# .net error-handling

我完全厌倦了我一直在编写的所有错误代码,所以看起来我真的需要理解处理异常的正确方法。

让我们考虑以下示例:

interface IDataSource
{
  string ReadData(int offset);
}

class FileDataSource : IDataSource {...}
class DatabaseDataSource : IDataSource {...}

如果我正在使用实现IDataSource的类对象,那么如果ReadData()由于某种原因失败,我可以期待捕获的异常是什么?

FileDataSource可能会失败,因为没有文件,或文件小于偏移量。 DatabaseDataSource可能会失败,因为它无法连接到数据库,或者该数据库中没有必需的表。

当我这样做时:

var data = dataSource.ReadData(10);

我应该抓到什么?从另一方面来说,如果我正在实施一个新的类FakeDataSource,如果发生了不好的事情,我应该扔什么?

我有一种感觉,当我从FileNotFoundExceptionFileDataSource SqlException抛出DatabaseDataSource时,这意味着封装违规,因为我知道实施细节。

是否所有IDataSource引发的异常都绑定到此接口?我的意思是 - 当我定义任何新的抽象时,我是否还应该定义相关的异常?像这样:

interface IDataSource { ... }    
abstract class DataSourceException : Exception { ... }

因此,当有人决定实施IDataSource时,他应该只抛出DataSourceException。那是对的吗?那么offset的错误值怎么样?我还应该抛出DataSourceException s或标准(.NET)ArgumentOutOfRangeException吗?

对于您认为值得的错误处理文章的任何链接也会表示赞赏。

5 个答案:

答案 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编译器。毕竟,当您想要处理原因时更有用:FileNotFoundExceptionDataSourceException

所以我的答案是:除非你知道如何处理异常,否则不要抓住!

答案 3 :(得分:1)

我认为没有任何针对抛出异常的具体规则。它可能是一个选择,对程序员来说非常主观。话虽这么说,你可以有不同的选择。就像你提到的那样,你可以抛出内置的异常,也可以从属于一个组的类中抛出自定义异常,比如实现接口。

在这种情况下,您可能最好抛出自己的自定义异常,因为您确切知道它来自何处以及如何处理它。 (没有真正的方法可以通过接口模型强制执行此操作)同样,它取决于当它冒泡时你正在做什么。对此没有严格的规则,但想法是你应该优雅地处理异常。我怀疑你是在编写有缺陷的代码。由于许多原因,例外情况发生。数据库连接失败不在您手中。在大多数情况下,数据库关闭时应用程序可能无法运行。除了捕获异常,记录,通知所需的组以及正常失败之外,您无法做很多事情。错误的应用程序将向用户显示服务器信息,而不会通知任何人,因此客户必须致电进行修复。

PS:我不相信从filedatasource(或来自sqlds的相应的一个)找不到抛出文件是违反封装的。该类应该知道实现细节,因为它是一个特定的目的类。

答案 4 :(得分:0)

你应该抛出任何描述发生的事情以及操作失败的原因。你应该只期望你真正可以处理的异常 - 永远不会捕获异常只是为了捕获它们。