捕获.NET中的基础Exception类
我一直在听
catch (Exception ex)
这是一种不好的做法,但是,我经常在事件处理程序中使用它,例如,某个操作可能会进入网络,从而可能出现许多不同类型的故障。在这种情况下,我会捕获所有异常,并在消息框中向用户显示错误消息。
这被认为是坏习惯吗?除此以外,我无能为力了:我不想让它停止应用程序,用户需要知道发生了什么,并且我处于代码的顶层。我还应该做什么?
编辑:
人们说我应该仔细检查所有堆栈并专门处理错误,因为例如StackOverflow异常无法得到有意义的处理。但是,停止该过程是最糟糕的结果,我希望不惜一切代价避免这种情况。如果我无法处理StackOverflow,那么结果将不比根本不捕获异常更糟糕,并且在99%的情况下,就我而言,通知用户是最不坏的选择。
另外,尽管我尽了最大的努力来解决所有可能抛出的异常,但是在大型代码库中,我很可能会错过一些异常。对于大多数人来说,最好的防御仍然是告知用户。
解决方案
如果我们重新引发无法正常处理的异常,那是完全可以的。如果仅捕获异常,则可能会在我们不期望的代码中隐藏错误。如果捕获异常以显示它们(并绕过die-and-print-trace-to-stderr行为),那是完全可以接受的。
从不应该在任何地方都这样做的意义上讲,这是一种不好的做法。
在这种情况下,我认为这是唯一合理的解决方案,因为例外可能确实是任何事情。唯一可能的改进是在捕获特定异常情况下的所有内容之前添加额外的处理程序,以便我们可以对异常进行某些处理。
不;在这种情况下,如果我们不希望暂停程序,则我们无能为力,并且只要我们正确地登录并且不将其隐藏起来就可以了,在顶层是正确的选择
坏习惯是
catch (Exception ex){}
和变体:
catch (Exception ex){ return false; }
等等。
在顶层捕获所有异常并将它们传递给用户(通过记录它们或者在消息框中显示它们,这取决于我们是编写服务器还是客户端应用程序),这是正确的选择。
在代码的最高级别捕获异常是完全有意义的。只要不需要基于异常类型执行任何其他逻辑,就可以捕获基本的Exception类型。
另外,请确保我们显示的是友好的常规错误消息,并且未显示实际异常的消息。这可能会导致安全漏洞。
重要的是要了解整个应用程序中异常的路径,而不仅仅是随便抛出或者捕获它们。例如,如果我们捕获的异常是内存不足怎么办?我们确定在这种情况下将显示对话框吗?但是,定义最后一个异常点并说我们永远不希望错误传播到该点之外当然是可以的。
当我看见
catch (Exception ex)
我的手开始摸索。几乎没有任何借口来抓住基本Exception。我想到的唯一有效案例是:
1)第三方组件引发异常(该死的是作者)
2)非常顶级的异常处理(作为最后的手段)(例如,在WinForms应用程序中处理"未处理的"异常)
如果我们发现可能发生许多不同类型的异常的情况,则这是不良设计的一个好兆头。
我不同意阿明·罗纳赫(Armin Ronacher)。如果引发StackOverflow异常,行为会如何?尝试执行其他操作可能会导致更严重的后果。仅当我们可以以有意义和安全的方式处理异常时,才捕获异常。捕获System.Exception以覆盖可能的异常范围是完全错误的。即使当我们重新扔它。
我们应该捕获与我们正在执行的操作有关的异常。如果我们查看调用的方法,我们将看到它们引发了哪些异常,并且我们希望对这些异常保持更具体的了解。我们应该有权了解调用的方法可能引发哪些异常,并适当地处理它们。
而且...比起一个大试钩更好,在需要的地方进行试钩。
try { myThing.DoStuff(); } catch (StuffGoneWrongException ex) { //figure out what you need to do or bail }
也许并不是那么紧密,但这取决于我们正在做的事情。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。请记住,工作不仅是将其编译并放置在某人的桌面上,我们还想知道如果某件事发生了什么会中断以及如何修复它。 (在此处插入有关跟踪的咆哮声)
我认为发布者指的是这样的异常处理:
try {something} catch (SqlException) {do stuff} catch (Exception) {do other stuff}
这里的想法是,我们要首先捕获更具体的错误(如SqlException)并适当地处理它们,而不是始终依赖于所有通用的Exception。
传统观点认为,这是进行异常处理的正确方法(并且单独的Catch(Exception ex)不好)。实际上,这种方法并不总是有效,特别是当我们使用别人编写的组件和库时。
基于组件在开发环境中的行为,这些组件在生产中通常会引发与代码所期望的异常类型不同的异常,即使两种环境中的基本问题相同。这是ASP.NET中一个非常常见的问题,经常导致我使用裸装的Catch(ex ex Exception)块,它不在乎抛出什么类型的异常。
从理论上讲,结构化异常处理是一个好主意。实际上,在我们控制的代码域内,这仍然是一个好主意。一旦我们介绍了第三方的内容,有时效果就会很差。
我们相当多地将Catch ex用作Exception(VB.Net变体)。我们将其记录下来,并定期检查我们的日志。找出原因并解决。
我认为一旦处理生产代码,Catch ex as Exception就可以完全接受,并且我们可以通过一种通用的方式优雅地处理未知异常。就我个人而言,在完成模块/新功能并针对测试中发现的任何异常进行专门处理之前,我不会投入一般的处理。这似乎是两全其美。
我发现这样的论点是,过于笼统的教条通常是不好的。他们像其他所有事物一样都有自己的位置。
那个地方不是库代码,也不是我们为应用程序定制开发的类。正如许多人所提到的,该地方是应用程序的最高层,如果引发任何异常,则很可能是意外的。
这是我的一般规则(和所有规则一样,在适当的时候也会被打破):
我使用类和定制库来完成应用程序中的大部分工作。这是基本的应用程序体系结构-请注意,这真的很基本。这些家伙试图处理尽可能多的异常,如果他们确实无法继续,则将最具体的可用类型返回到UI。
在UI上,我倾向于总是从事件处理程序中捕获所有信息。如果有合理的期望可以捕获特定的异常,并且我可以对此做一些事情,那么我可以捕获特定的异常并优雅地处理它。但是,这必须先于一切,因为.NET将仅使用与异常匹配的第一个异常处理程序。 (始终从最具体到最通用!)
如果除了错误输出(例如,数据库处于脱机状态)之外,我对异常无能为力,或者如果异常确实是意外的,则捕获所有异常将其接收,记录并快速安全地失败,并显示一条一般错误消息在死亡之前显示给用户。 (当然,某些类的错误几乎总是会不恰当地失败-OutOfMemory,StackOverflow等。我很幸运,到目前为止还没有必要处理产品级代码中的那些错误!)
抓住一切都有它的位置。那个地方不是要隐藏异常,那个地方不是要尝试恢复(因为如果我们不知道所捕获的内容,怎么可能恢复),那个地方并不是要防止错误显示给用户,同时允许应用程序将继续在未知和错误的状态下执行。
抓住所有人的位置将是万不得已的陷阱,这是一个陷阱,以确保通过我们精心设计和精心保护的防御措施(如果有的话),至少要对它进行正确记录并可以进行干净出口。如果我们在较低级别上没有精心设计和精心保护的防御措施,那么这是不好的做法,而在较低级别上这是非常不好的做法,但作为最后的选择,在我看来,这是不可接受的,但通常是正确的做法。
是的,可以在应用程序的顶层捕获基本Execption,这就是我们正在做的事情。
我们得到的强烈反应可能是因为在其他任何级别上捕获Base异常几乎总是错误的。特别是在图书馆中,这将是非常糟糕的做法。
很多时候捕获到异常以释放资源,是否(重新)抛出异常并不重要。在这种情况下,我们可以避免尝试捕获:
1)对于一次性对象,我们可以使用" using"关键字:
使用(SqlConnection conn =新的SqlConnection(connStr))
{
//代码
}
一旦超出使用范围(通常,通过return语句或者通过异常),就会自动在对象上调用Dispsose方法。换句话说,这就像try / finally构造。
2)在asp.net中,我们可以拦截Page对象的Error或者UnLoad事件以释放资源。
我希望我能!
我在回应"但是,停止进程是最糟糕的结果……"
如果我们可以通过运行不同的代码(使用try / catch作为控制流),重试,等待和重试,使用其他但等效的技术(即回退方法)重试来处理异常,则一定要这样做。
进行错误消息替换和日志记录也很好,除非是伪礼貌的被动攻击性的"联系管理员"(当我们知道没有管理员时,如果有管理员则对此无能为力! )但是,执行完此操作后,应用程序应结束,即与未处理的异常相同的行为。
另一方面,如果打算通过将用户返回到可能已破坏其状态的代码线程来处理异常,那么这比终止应用程序并让用户重新开始要糟糕。用户必须从头开始重新启动还是让用户销毁数据更好?
如果在确定可以从中提取资金的帐户的模块中出现意外异常,我是否真的要记录并报告异常并将用户返回到提取资金屏幕?就我们所知,我们只是授予他从所有帐户中提款的权利!
捕获可以处理的异常很好。但是有时也会发生这样的情况,由于不稳定的环境或者用户只是正确地执行了该过程,因此应用程序会遇到意外的异常。尚未在代码中列出或者处理代码。有没有一种方法可以从app.config文件中捕获未处理的异常并显示常见错误?
还将详细信息异常消息放在日志文件中。
我一直在努力处理一些例外情况,以下是我目前正在遵循的实现结构:
- 在"尝试/捕获"之外将所有内容缩小为Nothing / String.Empty / 0等。
- 将"尝试/捕获"中的所有内容初始化为所需的值。
- 首先捕获最具体的异常,例如FormatException,但保留基本的异常处理作为最后的选择(记住,我们可以有多个catch块)
- 几乎总是抛出异常
- 让global.asax中的Application_Error子代码优雅地处理错误,例如调用自定义函数以将错误的详细信息记录到文件中并重定向到某些错误页面
- 杀死所有我们在Final方块中变暗的物体
我认为最近不"适当"处理异常是可以接受的一个示例是使用通过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