简体   繁体   English

我应该/应该抛出什么?

[英]What should I throw / expect to be thrown?

I'm completely tired of all the buggy code I've been writing for all this time and so it looks like I really need to understand the correct approach for working with exceptions. 我已经完全厌倦了这段时间编写的所有错误代码,因此看起来我真的需要了解使用异常的正确方法。

Let's consider the following example: 让我们考虑以下示例:

interface IDataSource
{
  string ReadData(int offset);
}

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

If I'm using an object of class that implements IDataSource, what are the exceptions I can expect to catch if ReadData() fails for some reason? 如果我使用的是实现IDataSource的类的对象,如果由于某种原因ReadData()失败,我可以期望捕获哪些异常?

FileDataSource can fail because there's no file, or file is less than offset. FileDataSource可能会因为没有文件或文件小于偏移量而失败。 DatabaseDataSource can fail because it can't connect to the database, or there's no required table in that database. DatabaseDataSource可能会失败,因为它无法连接到数据库,或者该数据库中没有必需的表。

When I do this: 当我这样做时:

var data = dataSource.ReadData(10);

What should I catch? 我应该抓住什么? From the other side, if I'm implementing a new class FakeDataSource , what should I throw if something bad happens? 另一方面,如果我要实现一个新的类FakeDataSource ,如果发生不好的情况该怎么办?

I have a feeling that when I throw FileNotFoundException from FileDataSource or SqlException from DatabaseDataSource it means encapsulation violation, since I know implementation details. 我有一种感觉,当我从FileDataSource抛出FileNotFoundException或从DatabaseDataSource SqlException ,这意味着封装违规,因为我知道实现细节。

Should all the exceptions thrown by any IDataSource be bound to this interface? 是否应该将任何IDataSource抛出的所有异常都绑定到此接口? What I mean is - when I define any new abstraction, should I also define the related exceptions? 我的意思是-当我定义任何新的抽象时,是否还应该定义相关的异常? Like this: 像这样:

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

So, when someone decides to implement IDataSource , he should only throw DataSourceException s. 因此,当某人决定实现IDataSource ,他应该只抛出DataSourceException Is that correct? 那是对的吗? What about wrong values for offset - should I still throw DataSourceException s or standard (.NET) ArgumentOutOfRangeException is OK? offset值的错误如何处理-我还是应该抛出DataSourceException或标准(.NET) ArgumentOutOfRangeException正常吗?

Would also appreciate any links to the articles about error handling you consider worthy. 也将感谢您认为值得的有关错误处理的文章的任何链接。

You should only catch the exceptions that you can handle. 您应该只捕获可以处理的异常。 If your IDataSource throws FileNotFoundException or SqlException and you can't handle it, don't catch it. 如果您的IDataSource抛出FileNotFoundException或SqlException并且您无法处理它,请不要捕获它。

When throwing exceptions, you should throw the type of exception that best describes the problem. 引发异常时,应引发最能描述问题的异常类型。 There are three things that you want to know from a thrown exception . 您想从抛出的异常中了解三件事

  • What went wrong? 什么地方出了错?
  • Where did it go wrong? 哪里出错了?
  • Why did it go wrong? 为什么会出错?

When exceptions are used effectively, what is answered by the type of exception thrown, where is answered by the exception stack trace, and why is answered by the exception message. 当有效地使用异常时,由抛出的异常类型回答什么,由异常堆栈跟踪回答哪里,由异常消息回答为什么。 If you find your exceptions aren't answering all three questions, chances are they aren't being used effectively. 如果您发现自己的例外没有回答所有三个问题,则很可能是它们没有得到有效利用。 Three rules will help you make the best use of exceptions when debugging your programs. 调试程序时,三个规则将帮助您充分利用异常。 These rules are: be specific, throw early, and catch late. 这些规则是:具体,早扔和迟到。

In most cases use commonly defined exceptions rather than creating your own. 在大多数情况下,使用通用定义的异常,而不是创建自己的异常。 People will be familiar with them and understand what they mean without additional explanation. 人们将不加解释地熟悉它们并理解其含义。 However, creating your own exception type may be helpful in cases where it is desirable to encapsulate details for a particular module. 但是,在需要封装特定模块的详细信息的情况下,创建自己的异常类型可能会有所帮助。 When taking this approach, wrap the original exception within the exception for your module so that the information doesn't get lost when it is rethrown. 采用这种方法时,请将原始异常包装在模块的异常中,以使信息在重新抛出时不会丢失。

Exceptions should only be thrown in exceptional conditions where your library/application cannot do what it's being asked to do and needs help determining if and how it should recover. 仅在您的库/应用程序无法执行被要求执行的操作且需要帮助确定是否以及如何恢复的例外情况下,才应引发异常。

Your exceptions should identify exactly what happened. 您的例外情况应能准确说明发生了什么。 They should be generic enough to indicate what the top level issue is with a message that indicates exactly what the issue was. 它们应具有足够的通用性,以通过指出问题确切内容的消息来指示顶级问题。 Break down your exception categories based on the different types of operations. 根据不同类型的操作细分异常类别。

If you have any trouble making your connection, throw an ConnectionException, and use the message to specify why the exception is being thrown. 如果在建立连接时遇到任何麻烦,请引发ConnectionException,然后使用该消息指定引发异常的原因。 If you can't connect to the database, then the message should indicate so. 如果您无法连接到数据库,则该消息应指示连接。 Incorrect username? 用户名不正确? Incorrect password? 密码错误? Let the caller know. 让来电者知道。 And so on. 等等。

If there is an issue with your query, as would be the case if the table you're trying to query does not exist, then throw a QueryException. 如果您的查询存在问题,例如您要查询的表不存在,则抛出QueryException。

Limiting every exception in your DataSource implementation to a DataSourceException is almost as bad as throwing every exception as type Exception. 将DataSource实现中的每个异常都限制为DataSourceException几乎和抛出Exception类型的抛出每个异常一样糟糕。 You wouldn't do that, would you? 你不会那样做,对吗?

I also wouldn't bother wrapping the exceptions in a DataSourceException. 我也不会打扰将异常包装在DataSourceException中。 I think this adds far more complexity to your implementation than is necessary. 我认为这给您的实现增加了不必要的复杂性。 It'll also make readability a little bit more complicated as you'll have a bunch of code that looks like this: 它还将使可读性稍微复杂一点,因为您将拥有一堆看起来像这样的代码:

new DataSourceException(new ArgumentException("Range is invalid"), "Range is invalid");

Or this: 或这个:

try {
  ...
} catch (Exception e) {
  throw new DataSourceException(e, e.Message);
}

Then for all of the classes that consume your implementation, you'll have to catch the exception, then look at the inner exception to check and see if it's something that you want to handle gracefully. 然后,对于所有消耗实现的类,您必须捕获异常,然后查看内部异常以检查并查看是否要优雅地处理它。

Does that make sense? 那有意义吗?

Why should you want to catch anything at that level? 为什么要在该级别上捕捉任何东西?

You should catch for one reason only: You know how to handle the exception. 您应该只因为一个原因而捕获:您知道如何处理异常。

Some say you can catch if you want to log, but you should rethrow the exception in that case. 有人说如果您想登录就可以捕获,但是在这种情况下应该重新抛出异常。 But this means repeating the same log lines all over the place - a better way is to log once at the main loop. 但这意味着到处重复相同的日志行-更好的方法是在主循环中记录一次。 The exception contains a stack trace anyway. 无论如何,该异常都包含堆栈跟踪。

Catching and rethrowing a DataSourceException is a typical Java solution, with one goal only: satisfying the Java compiler. 捕获并重新抛出DataSourceException是一种典型的Java解决方案,其目的只有一个:满足Java编译器的要求。 After all, what is more useful when you want to handle the cause: a FileNotFoundException , or a DataSourceException ?! 毕竟,当您要处理原因时,更有用的是FileNotFoundExceptionDataSourceException吗?

So my answer is: Do not catch unless you know how to handle the exception! 所以我的回答是: 除非您知道如何处理异常,否则不要抓住!

I don't think there are any specific rules for throwing exceptions, per say. 我不认为有任何特定的异常抛出规则。 Its probably more of a choice, very subjective for programmers. 它可能是更多选择,对程序员来说非常主观。 That being said, you can have different options. 话虽如此,您可以有不同的选择。 Like you mention, you can either throw built in exceptions or throw your custom exception from the classes that belong to a group, say implementing interface. 就像您提到的那样,您可以引发内置异常,也可以从属于某个组的类(例如实现接口)引发自定义异常。

In this scenario, you are probably better off throwing your own custom exception as you know exactly where it is coming from and how to handle it. 在这种情况下,您最好抛出自己的自定义异常,因为您确切知道异常的来源以及如何处理它。 (There is no real way to enforce this through the interface model) Again, it depends on what you are doing with the exception when it bubbles up. (没有通过接口模型强制执行此操作的实际方法。)再次,这取决于您在异常冒泡时对异常的处理方式。 There is no hard and fast rule for this but the idea is that you should handle the exception gracefully. 对此没有硬性规定,但是我们的想法是应该优雅地处理异常。 I doubt you are writing buggy code. 我怀疑您正在编写错误的代码。 Exceptions happen due to many reasons. 发生异常的原因有很多。 Database connection failure is not in your hands. 数据库连接失败不在您手中。 In most cases, the application may not work when the database is down. 在大多数情况下,数据库关闭时,应用程序可能无法运行。 There is not much you can do other than catch the exception, log, notify the required group, and fail gracefully. 除了捕获异常,记录日志,通知所需组并正常失败之外,您无能为力。 A bad application will show server information to the user and not notify anyone so the customer will have to call to fix it. 错误的应用程序将向用户显示服务器信息,而不会通知任何人,因此客户将不得不致电对其进行修复。

PS: I do not believe throwing file not found from filedatasource (or corresponding one from sqlds) is a violation of encapsulation. PS:我不认为抛出从filedatasource(或sqlds中对应的文件)中找不到的文件不违反封装。 That class is supposed to know the implementation details since it is a specific purpose class. 由于该类是特定用途的类,因此应该知道实现细节。

You should throw whatever describes what happened and why operation failed. 您应该抛出任何描述发生了什么以及操作失败的原因的信息。 You should only expect the exception you really can handle - never catch exceptions just to catch them. 您应该只期望可以真正处理的异常-绝不要仅仅为了捕获异常就捕获异常。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM