如何使用try catch进行异常处理是最佳实践。
在软件开发中,异常是一种阻塞错误。最佳实践应该是在任何类型的错误情况下都不抛出异常,除非是阻塞错误。如果错误是阻塞错误,那么就抛出异常。一旦异常已经被抛出,就没有必要隐藏它,因为它是异常的;让用户知道它(你应该重新格式化整个异常,以便在用户界面中对用户有用)。作为软件开发人员,你的工作是努力防止可能导致异常的异常情况。也就是说,异常不能被静音,但必须被避免。例如,如果你知道某个整数输入可能具有无效的格式,请使用int.TryParse而不是int.Parse。有很多情况下,你可以这样做,而不仅仅是说“如果失败,只需抛出异常”。抛出异常是昂贵的。如果最终还是抛出了异常,那么在抛出异常后将异常写入日志而不是在一个最佳实践中捕获它是在第一次出现异常处理程序中捕获它。例如:
ASP.NET:Global.asax Application_Error
其他情况:AppDomain.FirstChanceException事件。
我的观点是,对于处理可能将异常转换为另一个异常的特殊情况或者当你希望对一个非常特殊的情况进行“静音”时(一个库错误抛出一个无关的异常,你需要静音以绕过整个错误),局部的try/catch更适合。对于其他情况:
尝试避免异常。
如果这不可能:首次机会异常处理程序。
或者使用PostSharp方面(AOP)。
首先,一个异常怎么可能不是一个错误呢?我们可以列举出1000种抛出异常的情况,最终,任何一种可能的情况都将是一个错误。异常是错误,因为在一天结束的时候,它是一个收集诊断信息的对象--它有一个消息,并且在某些事情出错的时候发生。没有人会在没有异常的情况下抛出异常。异常应该是阻塞错误,因为一旦它们被抛出,如果你不试图进入使用try/catch和异常来实现控制流的情况,它们意味着你的应用程序/服务将停止进入异常情况的操作。此外,我建议每个人都检查由Martin Fowler发布(由Jim Shore撰写)的故障快速范例。这是我在很久以前就知道如何处理异常的方式,甚至在此之前,我就已经了解到这个文件了。通常情况下,异常会切断一些操作流程,并且会被处理以将其转换为人类可理解的错误。因此,似乎异常实际上是处理错误案例的更好范例,并且可以通过处理它们来避免应用程序/服务完全崩溃并通知用户/消费者发生了错误。如果你的应用程序在没有将数据持久化到数据库的情况下可以离线工作,那么你不应该使用异常,因为使用try/catch实现控制流被认为是一种反模式。离线工作是一种可能的用例,所以你要实现控制流来检查数据库是否可访问,而不是等到它不可访问为止。解析事情也是一个预期的情况(不是异常情况)。如果你预料到这一点,你不会使用异常来进行控制流!你从用户那里获取一些元数据,以了解他/她的文化是什么,然后使用格式化程序。.NET支持这个,其他环境也支持,如果你期望一个特定于文化的使用这个应用程序/服务,你应该避免使用异常来进行格式化。这篇文章只是作者的观点或观点。既然维基百科也可能只是文章作者的观点,我不会说它是教条,但是看看“按异常编码”文章在某些段落中的说法:使用这些异常来处理特定的错误以继续程序被称为异常编码。这个反模式会迅速降低软件的性能和可维护性。它还在某些地方说:不正确的异常使用通常会导致软件中的进一步问题,其中包括不正确的异常使用。除了使用异常处理一个唯一的问题外,不正确的异常使用还在软件发现问题后执行代码。这种糟糕的编程方式在许多软件语言中都类似于goto方法,但只在检测到软件中的问题后出现。老实说,我认为不能开发软件而不认真考虑使用案例。如果你知道...你的数据库可能离线...某个文件可能被锁定...某个格式可能不被支持...某个域验证可能失败...你的应用程序应该在离线模式下工作...无论是什么用例......你都不会使用异常。你会使用常规的控制流来支持这些用例。如果没有覆盖到一些意外的用例,你的代码将会快速失败,因为它会抛出一个异常。对,因为异常是一个异常情况。另一方面,最后,有时你会抛出预期的异常来处理异常情况,但你不会抛出它们来实现控制流。你这样做是因为你希望通知上层你不支持某个用例或者你的代码无法处理某个给定的参数或环境数据/属性。
异常处理中使用try-catch是最佳实践的原因是为了避免隐藏问题,在以下三种情况下使用try-catch是合理的:
1. 对于已知异常,应该尽可能地在低层进行处理。然而,如果你预期会出现异常,通常最好先进行逻辑检查,而不是使用特定的try-catch。
2. 如果需要在异常发生时执行某些操作(例如日志记录或回滚事务),则重新抛出异常。
3. 对于未知异常,应该尽可能地在高层进行处理,只有UI或公共API应该处理异常而不重新抛出异常。
在连接到远程API时,如果你知道会出现某些错误并已经做好了相应的处理,那么可以使用第一种情况的try-catch。
在尝试将数据保存到数据库时,如果保存失败需要回滚操作,那么可以使用第二种情况的try-catch。
在UI中,我们不希望完全不处理异常,但也不希望隐藏异常。在这种情况下,可以使用第三种情况的try-catch。
大多数API或UI框架都有处理第三种情况的通用方式。例如,ASP.Net具有一个黄色错误屏幕,它会显示异常的详细信息,但在生产环境中可以替换为更通用的消息。遵循这些最佳实践可以节省大量代码,并且错误记录和显示应该是配置决策而不是硬编码的。
这意味着第一种情况(已知异常)和第三种情况(单次UI处理)都有更好的模式。甚至第二种情况也可以用更好的模式替代,例如使用事务范围(rollback未在范围内提交的任何事务)使开发人员更难犯错误。
总之,最佳实践实际上是不使用try-catch块。在大多数情况下,都有更好的方式处理异常。你应该阅读整篇文章,如果你仍然不同意,或许更有建设性的做法是反驳我的支持论点之一,而不仅仅是表达你不喜欢我的结论。几乎每种.NET开发类型都有更适合处理全局异常的处理程序,这样更容易以一致的方式处理异常,也更容易在开发环境中让其崩溃(为什么有人想要在日志文件中查找堆栈跟踪?),我会首先介绍你的TLDR,并添加一些全局处理程序的示例(如ThreadException、Application_Error等)。捕获特定错误是合理的,但在每个方法中都包装try-catch和日志记录是不明智的。
如何使用try catch进行异常处理是最佳实践
异常处理策略:
1.通过挂接到Application.ThreadException事件来捕获所有未处理的异常,然后根据情况做出决定:
- 对于UI应用程序:弹出一个带有道歉信息的消息框(WinForms)
- 对于服务或控制台应用程序:将其记录到文件中(服务或控制台)
2.在每个运行外部代码的地方都使用try/catch:
- 所有由WinForms基础设施触发的事件(Load, Click, SelectedChanged...)
- 所有由第三方组件触发的事件
3.将以下操作放在try/catch块中:
- 所有可能无法正常工作的操作(IO操作,可能出现除以零的计算...)。在这种情况下,抛出一个新的ApplicationException("自定义消息", innerException)来跟踪实际发生的情况
4.尽量正确地分类异常。有一些异常需要立即显示给用户,有些需要一些额外的处理来避免级联问题(例如:在TreeView填充过程中,在finally部分中添加.EndUpdate)
5.使用静态方法在应用程序的顶级错误处理程序中处理异常是一种好的实践。
以下是一些用于处理捕获的异常的扩展方法的示例。它们被实现为可以链接在一起的方式,并且很容易添加自己的异常处理逻辑。
catch(Exception ex) { throw ex; }在C#中比冗余更糟糕(无论捕获的异常类型是什么)。要重新抛出异常,请使用throw;。前者会让异常看起来像是来自于throw ex,而后者会正确地来自于原始的throw语句。
为什么要挂接Application.ThreadException事件并在每个异常上包装catch(Exception ex) {ex.Log(ex);}?我可能同意前者是一个很好的实践,但后者增加了重复错误日志的风险,并且隐藏了异常的发生。此外,throw ex非常糟糕。
在记录throw时,哪些是有用的日志条目?ex.stacktrace还是ex.innerexception?对于执行某些操作而言,哪个是有用的信息?
MSDN建议不要在代码中抛出ApplicationException异常。
这是一些处理捕获的异常的扩展方法的示例。它们被实现为可以链接在一起的方式,并且很容易添加自己的异常处理逻辑。
对于使用try/catch进行异常处理的最佳实践,可以遵循以下几点:
- 挂接到Application.ThreadException事件来捕获所有未处理的异常
- 在每个运行外部代码的地方使用try/catch
- 将可能无法正常工作的操作放在try/catch块中,并抛出一个新的ApplicationException来跟踪实际发生的情况
- 根据需要正确分类异常,并进行适当的处理和记录
- 使用静态方法在应用程序的顶级错误处理程序中处理异常
- 避免使用catch(Exception ex) { throw ex; },而使用throw;重新抛出异常
- 不要在代码中抛出ApplicationException异常
- 使用扩展方法来处理捕获的异常,并根据需要记录或显示异常信息
通过遵循这些实践,可以更好地处理异常并提高代码的可靠性和可维护性。