试试Catch应该使用,除非它永远不应该使用?

时间:2014-06-19 07:07:06

标签: c# exception try-catch

所以我一直在研究如何进行try-catch-finally块,并且在我阅读的每篇文章中都有一些相互矛盾的信息。有人可以澄清吗?

一个常见的想法是不捕获代码中不知道如何处理的异常。异常会冒出来,直到它可能会到达一个全局异常处理程序,如果没有别的东西捕获它。因此,此时您向用户显示一条消息,指出发生了未知类型的异常,记录等等。

现在看完之后听起来这是你需要的唯一异常处理程序吗?您不应该将它用于流控制,因此您应该检查某些内容是否返回为null或无效导致异常并在代码中进行更正。即。测试null并在它可能导致异常之前做些什么。

但是还有其他一些事情,比如内存不足,你将无法测试,它只会在操作期间发生,并且会抛出异常。但我无法对此做任何事情,所以这会冒泡到全局处理程序。

我认为有一些我不知道,比如在处理文件时可能会有一些从外部代码抛出。文件未找到异常似乎可能经常出现,所以我会抓住它并在finally块中优雅地关闭我打开的与其他代码/处理相关的任何内容,然后通知用户并将其记录在那里?

你想要捕获异常的唯一原因是块的最后部分是为了确保在异常之前启动的任何内容在已知状态下关闭/完成?但是,即便如此,您仍然希望在执行这些任务后抛出此异常,以便全局异常处理程序通知用户,此时此代码没有重复点?

除了全局异常处理程序之外,您还可以使用try-catch-finally块来处理这些情况。

所以假设我在这里遗漏了一些东西,你可能想要尝试捕获一个特定类型的异常,然后用它做一些事情。我想不出你想在catch块中做什么,因为全局的会记录/通知用户,如果你有一个异常通常意味着代码没有继续处理。

有没有简单的方法可以知道哪些模块会抛出哪些异常?我认为我阅读的唯一方法是阅读MSDN或组件供应商文档,除了没有办法知道如果你正在寻找一个特定的(例如,你不确定为什么),你将尝试捕获什么异常

这个问题出现了,因为在我的应用程序中,我在try-catch块中有一段代码,结果是当发生异常时,要么是因为对象为null,要么是字符串无效。一旦我编写代码来处理这些场景,就不再需要try-catch块,如果遇到异常,现在代码无法恢复,所以应该记录它并让用户知道它可以修复。 / p>

但这违背了我一直在阅读的内容以及向我讲述的内容,糟糕的代码是没有try-catch块的代码。那么这一切是如何结合在一起的,我在这里错过了什么?

3 个答案:

答案 0 :(得分:2)

没有用于例外管理(提升和处理)的 true 指南。每个应用程序都必须决定应该使用什么级别的流量控制以及如何引发/处理异常。

一般规则是:

  • 例外情况下引发异常
  • 处理您可以处理的异常并为您的应用做有意义的事情
  • 异常提升可以用于流量控制,实际上它是您在处理设备时可靠地处理流量控制的唯一方法,因此硬件中断。 (打印机,账单验证器,文件传输......)

剩下的由你决定。异常管理的含义由您自己完成。

答案 1 :(得分:2)

问题的第一部分是正确的:您应该只捕获您知道如何处理的异常。否则,只需让它们冒泡,直到它们到达可以处理它们的代码。

(注意"句柄"并不意味着" log"或"显示错误"。这意味着纠正导致异常的问题,或以某种方式解决它。)

如果他们从未遇到可以处理它们的代码,或者它们是不可处理的异常(如OutOfMemory),那么它们最终将到达全局未处理的异常处理程序。您可以在此处记录异常(如果适用),向用户显示一般错误(如果适用),并且通常会终止应用程序。您不能简单地继续,好像什么都没发生 - 异常表明应用程序处于意外状态。如果你继续尝试,你就会崩溃,或者更糟。

  

我认为有一些我不知道,比如在处理文件时可能会有一些从外部代码抛出。文件未找到异常似乎可能经常出现,所以我会抓住它并在finally块中优雅地关闭我打开的与其他代码/处理相关的任何内容,然后通知用户并将其记录在那里?

FileNotFound是您想要在本地处理的异常的一个很好的例子。在尝试加载文件的同一方法(或者可能是UI代码中的一个级别)中,您将有一个catch块用于FileNotFound异常。如果合适,向用户显示友好的错误消息并要求他们选择其他文件。如果是内部代码,请放弃并尝试其他内容。无论你需要做什么。 FileNotFound在代码之外冒出来的原因很少。

这是 sort ,就像使用流控制的异常一样,但不可避免。无法避免对I / O使用异常(或错误代码),因此您只需要处理故障情况。在尝试打开文件之前,您可以尝试验证该文件是否存在 ,但这无法解决因文件被删除或无法访问文件之间无法访问的竞争问题验证码运行的时间以及实际尝试打开验证码的时间。所以你现在所做的就是在两个的地方重复你的错误处理代码,这几乎没有用。

在本地处理FileNotFound等异常。离投射的代码越远,你就越不可能对它做任何合理的事情。

除了与I / O相关的异常之外,另一个很好的例子是NotSupportedException。例如,如果您尝试调用不受支持的方法,则可能会出现此异常。您可能希望处理它并在catch块中包含可回溯到安全替代方案的代码。

  

你想要捕获异常的唯一原因是块的最后部分是为了确保在异常之前启动的任何内容在已知状态下关闭/完成?但是,即便如此,您仍然希望在执行这些任务后抛出此异常,以便全局异常处理程序通知用户,此时此代码没有重复点?

这不需要捕获异常。您可以拥有仅try块的finally块。不需要catch块。实际上,这正是 using语句实现的内容。如果在抛出异常时需要清理状态,则应实现IDisposable模式并将该对象的用法包装在using块中。

  

有没有简单的方法可以知道哪些模块会抛出哪些异常?我认为我阅读的唯一方法是阅读MSDN或组件供应商文档,除了没有办法知道如果你正在寻找一个特定的(例如,你不确定为什么),你将尝试捕获什么异常

正。但是,这不是一个真正的问题,因为您只是捕获了可以执行某些操作的异常。如果您不知道模块可以抛出特定异常,那么您显然无法编写可以处理该异常的代码。

文档将告诉您可能需要处理的所有重要异常,例如FileNotFound,SecurityException或您拥有的内容。

  

这个问题出现了,因为在我的应用程序中,我在try-catch块中有一段代码,结果是当发生异常时,要么是因为对象为null,要么是字符串无效。一旦我编写代码来处理这些场景,就不再需要try-catch块,如果遇到异常,现在代码无法恢复,所以应该记录它并让用户知道它可以修复。 / p>

首先避免例外始终是最佳选择。例如,如果您可以设计应用程序,以便无法使用null对象或无效字符串,那就太好了。这就是我们所说的健壮代码。在这种情况下,您不需要捕获这些异常,因为您无法处理它们。你想到你已经处理过这个问题,所以如果异常被抛出,那就是一个bug的标志。不要用catch块来掩饰它。

但有时,catch块仍然是必需的,并且您在catch块的内编写代码来处理问题。在这种情况下,可能没有理由重新抛出异常或记录异常,因此您没有任何代码重复。

  

但这违背了我一直在阅读的内容以及向我讲述的内容,糟糕的代码是没有try-catch块的代码。那么这一切是如何结合在一起的,我在这里错过了什么?

完全错了。我不知道你在哪里阅读,但这是无稽之谈。例外是特殊情况。如果你的代码遍布它的catch块,那就表明你做错了。要么您正在使用流量控制的例外,您就会在误导的尝试中吞噬异常,以提高可靠性,或者您不了解全局未处理的异常处理程序。< / p>

听起来不像你错过任何东西。

我唯一不得不提及的并不是严格适合您的任何问题,有时您可能希望捕获异常并将其重新抛出为不同的异常。最常见的情况是,如果您正在设计可重用代码库。在库内部,您可能会捕获内部异常,如果无法处理它们,请将它们重新抛出为常规异常。库的整个要点是封装,所以你不应该让异常冒泡,调用者不能对做任何事情。

答案 2 :(得分:1)

想象一下,您需要从FTP服务器下载一些文件,一个您无法控制的文件。当然,你不能相信其他人,所以你需要准备临时停机和such。因此,您将下载代码包装在try-catch - 块中,如果您找到WebException,则可以查找FtpStatusCode.ActionNotTakenFileUnavailableOrBusy,如果这是您只是重试的错误,则可以检查404。同样,如果您拨打网络服务429可能有问题,但NullPointerException意味着您稍等一会然后重试,因为您的费率有限。

通常,您可以了解经验或文档可以抛出哪些异常,但C#缺少已检查的异常。可以使用代码中的警卫正确处理ArgumentNullException或{{1}}之类的内容。但是其他一些事情,例如错误外部依赖关系有时可以被你捕获和处理,而不会使应用程序崩溃。