捕获.NET中的exception类

我一直听到这个

catch (Exception ex) 

然而,不好的做法是,我经常在事件处理程序中使用它,例如,操作可能会进入网络,从而可能出现许多不同类型的故障。 在这种情况下,我捕获所有exception并在消息框中向用户显示错误消息。

这被认为是不好的做法吗? 除了exception之外我无能为力:我不希望它停止应用程序,用户需要知道发生了什么,并且我处于代码的顶层。 我还应该做什么?

编辑:

人们说我应该查看调用堆栈并专门处理错误,因为例如StackOverflowexception无法有意义地处理。 但是,停止这个过程是最糟糕的结果,我想不惜一切代价阻止这一过程。 如果我无法处理StackOverflow,那么就是这样 – 结果将不会比没有捕获exception更糟糕,并且在99%的情况下,就我而言,通知用户是最不好的选择。

此外,尽管我尽最大努力解决可能引发的所有可能exception,但在大型代码库中,我可能会错过一些。 对于他们中的大多数人来说,最好的防御仍然是告知用户。

不好的做法是

 catch (Exception ex){} 

和变种:

 catch (Exception ex){ return false; } 

等等

捕获顶层的所有exception并将它们传递给用户(通过记录它们或在消息框中显示它们,具体取决于您是在编写服务器还是客户端应用程序),这是正确的做法去做。

我发现generics捕获总是不好过分教条的论点。 他们和其他一切一样,都有一席之地。

那个地方不是您的图书馆代码,也不是您为自己的应用程序定制开发的课程。 正如许多人所提到的,这个地方是应用程序的最高级别,如果引发任何exception,则很可能是意外的。

这是我的一般规则(和所有规则一样,它的设计在适当的时候被破坏):

我在应用程序中使用类和自定义构建的库来进行大部分提升。 这是基本的应用程序架构 – 非常基本,请注意。 这些人尝试尽可能多地处理exception,如果他们真的无法继续,请将最具体的类型提供给UI。

在UI中,我倾向于始终从事件处理程序中捕获所有内容。 如果有合理的期望捕获一个特定的exception, 并且我可以对它做些什么,那么我会抓住特定的exception并优雅地处理它。 这必须在catch all之前,因为.NET只会使用与您的exception匹配的第一个exception处理程序。 (总是从最具体到最通用的订购!)

如果我不能对exception做任何事情而不是错误输出(例如,数据库处于脱机状态),或者exception确实是意外的,那么catch all将接受它,记录它并快速失败,并带有一般错误消息在临终前显示给用户。 (当然,某些类型的错误几乎总是会失败 – OutOfMemory,StackOverflow等等。我很幸运,没有必要处理prod级代码中的那些…到目前为止!)

抓住一切都有它的位置。 那个地方不是为了隐藏exception,那个地方不是为了尝试和恢复(因为如果你不知道你抓到了什么,你怎么可能恢复),那个地方不是为了防止错误显示给用户同时允许你的应用程序继续在未知和糟糕的状态下执行。

抓住所有的地方是最后的手段,一个陷阱,以确保如果有任何东西通过你精心设计和保护良好的防御,至少它被适当记录,并可以做出干净的退出。 如果你没有在较低级别设置精心设计并且保护良好的防御是不好的做法,并且在较低级别的练习是非常糟糕的,但作为最后的手段,它(在我看来)不仅可以接受,但往往是正确的事情。

当我看见

 catch (Exception ex) 

我的手开始摸索着锤子。 几乎没有任何借口可以捕获基础exception。 只有我想到的有效案例是:
1)第三方组件抛出exception(该死的作者)
2)非常顶级的exception处理(作为最后的手段)(例如处理WinForms应用程序中的“未处理”exception)

如果您发现可能发生许多不同类型的exception的情况,那么这是设计糟糕的好兆头。

我不同意Armin Ronacher。 如果引发StackOverflowexception,您会如何表现? 尝试执行其他操作可能会导致更糟糕的后果。 仅在您能够以有意义且安全的方式处理exception时才捕获exception。 捕获System.Exception以涵盖可能的exception范围是非常错误的。 即使你重新投掷它。

在代码的最高级别捕获exception非常有意义。 只要您不需要根据exception类型执行任何不同的逻辑,捕获基本exception类型就可以了。

此外,请确保显示友好的常规错误消息,而不显示实际的exception消息。 这可能会导致安全漏洞。

从某种意义上讲,你不应该在任何地方都这样做是不好的做法。

在这种情况下,我认为这是唯一合理的解决方案,因为您的例外可能是真正的任何事情。 唯一可能的改进是在您捕获所有特定错误情况之前添加额外的处理程序,您可以对exception执行某些操作。

是的,可以在应用程序的顶层捕获基本Execption,这就是你正在做的事情。

你得到的强烈反应可能是因为在任何其他级别 ,捕获Baseexception几乎总是错误的。 特别是在一个图书馆,这将是非常糟糕的做法。

如果你重新提出你无法正常处理的exception,这是完全可以的。 如果你只是捕获exception,你可以隐藏你不期望的代码中的错误。 如果您捕获exception以显示它们(并绕过die-and-print-traceback-to-stderr行为),这是完全可以接受的。

我认为海报是指这样的exception处理:

 try {something} catch (SqlException) {do stuff} catch (Exception) {do other stuff} 

这里的想法是你想要首先捕获更具体的错误(比如SqlException)并适当地处理它们,而不是总是依赖于catch-all常规exception。

传统观点认为,这是进行exception处理的正确方法(并且单独的Catch(Exception ex)很糟糕)。 实际上,这种方法并不总是有效,特别是当您使用其他人编写的组件和库时。

根据组件在开发环境中的行为方式,这些组件通常会在生产中抛出不同类型的exception,即使基础问题在两种环境中都相同。 这是ASP.NET中一个非常常见的问题,并且经常让我使用裸Catch(Exception ex)块,它不关心抛出什么类型的exception。

结构化exception处理在理论上是一个好主意。 实际上,在您控制的代码域中,它仍然是一个好主意。 一旦你介绍第三方的东西,它有时不能很好地工作。

我们使用Catch ex作为Exception(VB.Net变体)相当多。 我们记录它,并定期检查我们的日志。 追查原因,并解决。

我认为, 一旦您处理生产代码,Catch ex as Exception就完全可以接受了,并且您有一般方法可以优雅地处理未知exception。 就个人而言,在完成模块/新function之前,我没有把通用捕获放入,并对我在测试中发现的任何exception进行专门处理。 这似乎是两个世界中最好的。

没有; 在这种情况下,如果你不想停止程序那么你就无法做其他任何事情,只要你正确登录而不是将它隐藏在希望咧嘴中,顶层就是正确的地方。

重要的是通过您的应用程序了解exception的路径,而不是随意抛出或捕获它们。 例如,如果您捕获的exception是内存不足怎么办? 你确定在那种情况下会显示你的对话框吗? 但是定义一个最后一个exception点并且说你永远不希望错误传播超过那个点肯定是好的。

您应该捕获与您正在做的事情相关的例外情况。 如果你看一下你调用的方法,你会看到它们抛出的exception,并且你想要更加具体。 您应该有权知道您调用的方法可能引发的exception,并适当地处理它们。

而且……比尝试一次大尝试更好,尝试并抓住你需要捕获的地方。

 try { myThing.DoStuff(); } catch (StuffGoneWrongException ex) { //figure out what you need to do or bail } 

也许不是这么紧密,但这取决于你在做什么。 请记住,这项工作不仅仅是编译它并将其放在桌面上,你想知道如果有什么东西会破坏什么以及如何修复它。 (在这里插入关于追踪的咆哮)

很多时候exception都被用来释放资源,如果(重新)抛出exception并不重要。 在这些情况下,你可以避免尝试捕获:

1)对于Disposable对象,您可以使用“using”关键字:using(SqlConnection conn = new SqlConnection(connStr)){// code}一旦超出使用范围(通常或通过return语句或exception),Dispsose方法在对象上自动调用。 换句话说,就像尝试/最终构建一样。

2)在asp.net中,您可以拦截Page对象的Error或UnLoad事件来释放您的资源。

我希望我帮助你!

我回应“然而,停止这个过程是最糟糕的结果……”

如果你可以通过运行不同的代码来处理exception(使用try / catch作为控制流),重试,等待和重试,使用不同但等效的技术(即回退方法)重试,那么一定要这样做。

做错误消息替换和日志记录也很好,除非伪礼貌 – 被动 – 主动“联系你的管理员”(当你知道没有管理员,如果管理员无法做任何事情!但是在你这样做之后,应用程序应该结束,即与未处理的exception相同的行为。

另一方面,如果您打算通过将用户返回到可能破坏其状态的代码线程来处理exception,我会说这比结束应用程序并让用户重新开始更糟糕 。 用户是否最好在开始时重新启动或更好地让用户销毁数据?

如果我在模块中遇到意外的exception,确定哪些帐户可以从中提取资金,我是否真的想记录并报告exception并将用户返回到取款屏幕? 据我们所知,我们只是授予他从所有账户中提取资金的权利!

这样可以捕获您可以处理的exception。 但有时也会发生由于环境不稳定或用户只是正确执行该过程,应用程序会遇到意外exception。 您尚未在代码中列出或处理的内容。 有没有办法从app.config文件中捕获未处理的exception并显示常见错误?

还将详细信息exception消息放入日志文件中。

我一直在用exception工作,这是我目前正在遵循的实现结构:

  1. 在Try / Catch之外将所有内容变暗为Nothing / String.Empty / 0等。
  2. 将Try / Catch内的所有内容初始化为所需的值。
  3. 首先捕获最具体的exception,例如FormatException,但留在基本exception处理中作为最后的手段(你可以有多个catch块,记住)
  4. 几乎总是抛出exception
  5. 让global.asax中的Application_Error sub正常处理错误,例如调用自定义函数将错误的详细信息记录到文件中并重定向到某个错误页面
  6. 杀死你在Finally块中变暗的所有对象

我认为最近不能“正确”处理exception是可接受的一个例子是使用通过HTTP GET传递给页面的GUID字符串(strGuid)。 在调用New Guid(strGuid)之前,我本可以实现一个函数来检查GUID字符串的有效性,但它似乎相当合理:

 Dim myGuid As Guid = Nothing Try myGuid = New Guid(strGuid) 'Some processing here... Catch ex As FormatException lblError.Text = "Invalid ID" Catch ex As Exception Throw Finally If myGuid IsNot Nothing Then myGuid = Nothing End If End Try