So is it best to use try/catch?
什么时候最好用"试着接球"?当我用Try-and-Catch(一些甚至是-1我…)回答问题时,我得到了愤怒的回应,我在谷歌上找到了这篇文章和这个StackOverflow问题。
我举几个例子:
我有一个带有时区ID的下拉列表,当用户选择时区时,我正在更新数据库。在另一个应用程序中,我从数据库中提取这个值,然后重新计算用户当前的时间和日期。有一个选项是数据库中的数据拼写错误(数据库或bug中的硬编码更改)。在用户日期时间的转换方法中,我使用的是Try-and-Catch,有人告诉我这是错误的!.I可以使用for循环检查db的值,但每次转换日期和时间时都会花费更多的成本…
我必须声明XML文件是否使用此代码进行了良好的格式化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | protected bool IsValidXML(string xmlFile) { try { XmlDocument doc = new XmlDocument(); doc.LoadXml(xmlFile); } catch(XmlException ex) { ///write to logger return false; } return true; } |
我看不到任何其他检查XML文件的方法。
有时我在申请书上有个部分,我正在写一份文件。写入文件可能会导致执行,原因有很多,其他一些进程在写入或其他时正在使用此文件。所以通常我会使用以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 | using (StreamWriter w = new StreamWriter(fs)) { try { w.Write("** (Line)" + someValue +" **" + Environment.NewLine); w.Flush(); } catch(IOExeption ex){} finally { w.Close(); } } |
总之,我看到了一些使用"尝试-捕获"和"不使用"的方法。我看到的那篇文章中的一句话说,如果发生了异常,你需要知道它。但是,在处理通用应用程序时,大多数时候我知道会发生异常,但大多数时候我真的不知道为什么会发生异常,所以我以前无法捕获它(如我编写的示例),所以什么时候最好使用try-and-catch
在ASP.NET的同一级别中,该页有一个错误事件,您可以这样捕获:
1 |
事件是否与Try-Catch问题相同??
如何处理异常取决于异常的性质以及异常发生的上下文。
聪明人写过关于这个主题的优秀文章,我当然可以推荐:
- 如何用krzysztof cwalina设计异常层次结构
- 埃里克·利珀特的恼人例外
- API设计神话:例外是针对Krzysztof cwalina的"例外错误"
关于您的示例:
在案例1中,你可能真的想要防御性。但要确保在那个catch块中做一些明智的事情。
案例2看起来很合理,但是您阅读XML文档只是为了扔掉它。同样处理它是否更有意义?
在案例3中,您使用的是Try Finally,这绝对与Try Catch不同。在您的特定示例中,您甚至不需要它,因为using语句已经确保关闭文件。
例如1,看到代码会很有帮助。如果没有对你正在做的事情更具体的了解,很难发表评论。
例如2,该代码的问题在于它完全隐藏了任何错误条件。当然,我们会发现XML文件是好是坏…但是为什么XML是好的还是坏的?我们不知道。并不是说try/catch不好:而是整个过程都是在错误的层次上编写的。只需编写代码来读取XML文件,并对该代码进行错误处理。
例如3,您再次吞咽了任何有用的异常信息,实际上根本没有对您的Try/Catch做任何操作。您的
总之,我认为您在这里需要学习的不是Try/Catch块不好。吞咽异常信息是不好的。
在这个答案中,对您使用try/catch的批评不是反对一般使用try/catch,而是反对使用它的特定方式。
您应该捕获异常对象(即,使用catch(somethingAppenedException e),这样您就可以获得关于出错原因的可用信息。
您应该捕获特定的异常,而不是所有的异常。在datetime示例中,我将尝试查找每个调用可能引发的异常。然后,我会尝试找出是否有某种方法可以在不使用异常的情况下捕获这些错误-该代码中可能捕获的异常之一是ArgumentNullException。这是一个例外,但是我应该能够通过首先检查是否没有传递空区域ID来处理流中的这个问题。最后,如果无法在程序的正常流程中处理这些情况,我将捕获可能发生的特定异常,并尽可能接近异常源。
这看起来可能有点费力,但它确实节省了调试时间!
从个人的角度来看,我总是假设任何可能出错的东西都会出错,并相应地编写我的异常处理策略。我对其他代码的一个主要问题是,当您得到一个可能很容易被捕获的未经处理的异常时——如果它是生产代码,它看起来很混乱,那么它显示出对程序如何工作以及可能出错的事情的完全不了解。
我的方法可能是杀伤力过大,但如果您正在做的事情可能引发错误,那么它应该被捕获。我不喜欢让异常在堆栈中层出不穷,在顶层被捕获——我更喜欢在源代码处捕获异常,记录异常,然后,如果应用程序允许它继续运行,或者最坏的情况是优雅地失败,让用户(或其他开发人员)了解出了什么问题或原因。
对于Web应用程序,我们使用稍微不同的方法。记录错误(堆栈跟踪等),以便授权人员可以看到它,并向用户提供错误的缩减版本,该版本告诉用户发生了错误,但不告诉用户具体情况。
那是我的两便士。我不确定这种方法是正确还是错误,但它对我们有用,帮助我们生成更健壮的代码。
根据我的经验,任何外部操作(如与数据库或文件系统交互,或使用第三方组件)都应该有一个try-catch块。在我自己的代码中,我总是尝试将guard子句放入方法中,以限制我自己的代码产生的异常数,例如,如果您知道数据库不会接受Person对象的空名字,则应将guard子句添加到任何save方法的顶部。
简而言之,不要依赖异常来告诉您代码哪里出错,而是先尝试减少异常的可能性。
我想我可以用你的试一试问题来总结人们过去遇到的问题……抓住问题,并提供一些明晰的信息,以便将来帮助你。
前两个示例是由数据验证错误引起的。在传递给转换函数(示例1)或尝试写入XML文件(示例2)之前,应检查数据是否存在已知的验证错误。
异常处理中有很大的开销,所以只有在绝对必要的时候才会发生。Try…Catch模式设计用于处理意外(异常)错误,而不是标准数据验证。
示例3几乎是正确的用法。这是一种情况,在这种情况下,可能会发生一些您无法做好准备的意外事件,比如IOException。捕获特定异常被认为是错误的形式,因为它可能导致多个catch块或错过处理。捕获通用异常对象更好,异常处理代码可以确定和处理准确的异常类型。
您的try…catch块也应该封装streamwriter,因为最佳做法是每个方法只有一个try…catch。如果使用try…catch,它必须始终包含异常处理代码。空的catch块或空的finalize块的形式不好,不能帮助代码或任何试图在您之后维护它的人。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | try { using (StreamWriter w = new StreamWriter(fs)) { w.Write("** (Line)" + someValue +" **" + Environment.NewLine); w.Flush(); } } catch(Exception ex) { //exception handling code here } finally { //any clean up code here. The using statement makes it unnecessary for the streamwriter } |
希望所有这些都有助于回答一些问题,并使问题更加清晰。