为什么试着{…}最后{…}好; 尝试{…} catch {}不好?
我曾经看到有人说,没有任何争论就使用catch是一种糟糕的forms,特别是如果这种catch没有做任何事情:
StreamReader reader=new StreamReader("myfile.txt"); try { int i = 5 / 0; } catch // No args, so it will catch any exception {} reader.Close();
但是,这被认为是很好的forms:
StreamReader reader=new StreamReader("myfile.txt"); try { int i = 5 / 0; } finally // Will execute despite any exception { reader.Close(); }
据我所知,将清理代码放在finally块中,并在try..catch块之后放置清理代码之间的唯一区别是,如果在try块中有返回语句(在这种情况下,清理代码将在最后运行,但try..catch之后的代码将不会)。
否则,最后有什么特别的?
最大的区别是try...catch
会吞噬exception,隐藏发生错误的事实。 try..finally
会运行你的清理代码,然后exception将继续下去,由知道如何处理它的东西来处理。
“最后”是“为确保程序状态是健全的,你必须始终做的事情”。 因此,如果有任何exception可能会抛出程序状态的可能性,那么总是有一个好的forms。 编译器也竭尽全力确保您的Finally代码运行。
“Catch”是“我可以从这个例外中恢复”的陈述。 你只能从那些你可以纠正的例外中恢复过来 – 没有争论的话就会说:“嘿,我可以从任何事情中恢复过来”,这几乎总是不真实的。
如果有可能从每个exception中恢复过来,那么这真的是一个语义上的狡辩,关于你宣称你的意图是什么。 然而,事实并非如此,几乎可以肯定的是,高于你的框架将会更好地处理某些例外情况。 因此,最后使用,让你的清理代码免费运行,但仍然让更多的知识处理器处理这个问题。
因为当一行代码抛出一个exception时,你不会知道它。
使用第一块代码,exception将被简单地吸收 ,即使程序的状态可能错误,程序也将继续执行。
随着第二个块,exception将被抛出并冒泡, 但 reader.Close()
仍然保证运行。
如果没有预料到exception,那么不要放一个try..catch块,以后在程序进入不良状态时很难debugging,而且你也不知道为什么。
不pipe怎样,最后都是执行的。 所以,如果你的try块成功执行,如果你的try块失败,它将执行catch块,然后执行finally块。
此外,最好尝试使用以下结构:
using (StreamReader reader=new StreamReader("myfile.txt")) { }
由于using语句自动封装在try / finally中,stream将自动closures。 (如果你想要捕获exception,你将需要在using语句中jointry / catch)。
虽然以下两个代码块是相同的,但它们并不相同。
try { int i = 1/0; } catch { reader.Close(); throw; } try { int i = 1/0; } finally { reader.Close(); }
- “终于”是意图揭示的代码。 您向编译器和其他程序员声明,无论如何,此代码都需要运行。
- 如果你有多个catch块,你有清理代码,你最后需要。 没有最后,你会在每个catch块重复你的清理代码。 (干原则)
最后块是特别的。 CLR通过与catch块分离的finally块来识别和处理代码,CLR花费很大的精力来保证finally块将始终执行。 这不仅仅是编译器的语法糖。
我同意似乎在这里的共识 – 一个空的“catch”是不好的,因为它掩盖了try块中可能发生的任何exception。
另外,从可读性的angular度来看,当我看到一个“尝试”块时,我认为会有一个相应的“catch”语句。 如果您只是使用“try”来确保在“finally”块中取消分配资源,则可以考虑使用“using”语句 :
using (StreamReader reader = new StreamReader('myfile.txt')) { // do stuff here } // reader.dispose() is called automatically
您可以对任何实现了IDisposable的对象使用“using”语句。 该对象的dispose()方法在块的末尾被自动调用。
try..finally块仍然会抛出exception。 finally
,确保在抛出exception之前运行清理代码。
带有空捕获的try..catch将完全消耗任何exception,并隐藏它发生的事实。 读者将被closures,但是不知道是否发生了正确的事情。 如果你的意图是写我的文件呢? 在这种情况下,您不会将其添加到代码的这一部分, myfile.txt将为空。 所有的下游方法是否正确处理? 当你看到空文件时,你能够正确地猜出它是空的,因为抛出了一个exception? 最好抛出exception,让它知道你做错了什么。
另一个原因是try..catch这样做是完全不正确的。 你所说的这样做是:“无论发生什么事,我都能应付。” 怎么样StackOverflowException
,你可以清理后? 那么OutOfMemoryException
呢? 一般来说,你只应该处理你期望和知道如何处理的exception。
如果你不知道要捕捉什么exceptiontypes或如何处理它,那么有一个catch语句是没有意义的。 你应该把它留给一个更高级的来电者,可能有更多的情况知道该怎么做。
如果发生exception,应该还有一个finally语句,以便在抛出exception之前清理资源。
从可读性的angular度来看,更明确地告诉未来的代码阅读器“这里的东西是重要的,无论发生什么事情都需要做。 这很好。
另外,空的catch语句往往对他们有一定的“嗅觉”。 这可能是开发人员没有考虑到可能发生的各种例外以及如何处理这些例外的一个信号。
最后是可选的 – 如果没有资源清理,没有理由有“最后”块。
添加一个catch子句只是为了重新抛出exception是不好的做法。
最后,你可以清理资源,即使catch语句抛出exception直到调用程序。 用包含空的catch语句的示例,几乎没有区别。 但是,如果在你的捕捉中,你做了一些处理,抛出错误,甚至根本就没有捕捉到,最后仍然会运行。
取自: 这里
提高和捕捉exception不应该经常发生,作为一个方法的成功执行的一部分。 在开发类库时,客户端代码必须有机会在进行可能导致exception的操作之前testing错误情况。 例如,System.IO.FileStream提供了一个CanRead属性,可以在调用Read方法之前检查该属性,以防止发生潜在的exception,如以下代码片段所示:
Dim str As Stream = GetStream()If(str.CanRead)Then'code to read stream End If
在调用可能引发exception的特定方法之前是否检查对象状态的决定取决于对象的预期状态。 如果使用应该存在的文件path和应以读模式返回文件的构造函数创buildFileStream对象,则不需要检查CanRead属性; 无法读取FileStream将违反方法调用的预期行为,应引发exception。 相反,如果一个方法被logging为返回一个可能或不可读的FileStream引用,那么build议在尝试读取数据之前检查CanRead属性。
为了说明使用“运行到exception”编码技术可能导致的性能影响,强制转换(如果转换失败时抛出InvalidCastException)的性能将与C#as运算符进行比较,如果转换失败,则返回空值。 这两种技术的性能对于转换是有效的情况是相同的(参见testing8.05),但是对于转换无效的情况,使用转换导致exception,使用转换比使用转换慢600倍作为运营商(见testing8.06)。 exception投掷技术的高性能影响包括分配,抛出和捕获exception的成本以及exception对象的后续垃圾收集的成本,这意味着抛出exception的瞬间影响不是那么高。 随着越来越多的exception抛出,频繁的垃圾收集成为一个问题,所以经常使用exception抛出编码技术的整体影响将类似于testing8.05。
那么对于一个不好的做法,就是抓住你不打算处理的exception。 查看第5章关于 提高.NET应用程序性能和可伸缩性的 .Net性能 。 注意,你可能应该在try块中加载stream,这样,如果失败,你可以捕获相关的exception。 在try块之外创buildstream会影响其目的。
使用Try..Catch..Finally
,如果你的方法知道如何在本地处理exception。 exception发生在Try中,在Catch中处理,之后在最后清理完成。
如果你的方法不知道如何处理exception,但是需要清理一次,请使用Try..Finally
由此,exception被传播到调用方法,并在调用方法中存在任何合适的Catch语句时处理。如果在当前方法或任何调用方法中没有exception处理程序,则应用程序崩溃。
通过Try..Finally
最后确保本地清理完成之前传播到调用方法的exception。
可能有很多原因,例外执行速度很慢。 如果发生这种情况,您可以轻松地削减执行时间。
捕获所有exception的try / catch块的问题是,如果发生未知exception,程序现在处于不确定状态。 这完全违背了快速失败规则 – 如果发生exception,您不希望程序继续。 上面的try / catch甚至可以捕获OutOfMemoryExceptions,但这绝对是你的程序不能运行的状态。
尝试/ finally块允许您执行清理代码,同时仍然快速失败。 对于大多数情况,您只需要在全局级别捕获所有exception,以便您可以logging它们,然后退出。
只要不引发任何exception,您的示例之间的有效差异可以忽略不计。
但是,如果在“try”子句中抛出exception,则第一个示例将完全吞下它。 第二个例子将引发exception到调用堆栈的下一个步骤,因此所述示例的区别在于它完全掩盖了任何exception(第一个示例),另一个示例(第二个示例)保留了可能的后续处理的exception信息仍在执行“finally”子句中的内容。
例如,如果您将代码放在引发exception的第一个示例(无论是最初引发的还是新引发的示例)的“catch”子句中,读取器清理代码将永远不会执行。 最后执行, 不pipe在'catch'子句中发生了什么。
因此,“catch”和“finally”之间的主要区别在于,即使面对意外的exception,“finally”块的内容(有less数几个例外)可以保证执行,而任何代码“catch”条款(但在“finally”条款之外)不会带有这样的保证。
顺便说一下,Stream和StreamReader都实现了IDisposable,并且可以封装在一个“使用”块中。 '使用'块是try / finally(不'catch')的语义等价物,所以你的例子可以更简洁地expression为:
using (StreamReader reader = new StreamReader("myfile.txt")) { int i = 5 / 0; }
…当它超出范围时将closures并处理StreamReader实例。 希望这可以帮助。
尝试{…} catch {}并不总是不好的。 这不是一个常见的模式,但是当我需要closures资源时,无论如何closures资源,我都倾向于使用它,比如closures一个线程末尾的(可能)打开的套接字。
如果你会为程序员阅读C#,你就会明白,finally块是为了优化应用程序和防止内存泄漏而devise的。
CLR并没有完全消除泄漏…如果程序无意中保持对不需要的对象的引用,则会发生内存泄漏
例如,当您打开一个文件或数据库连接时,您的机器将分配内存来处理该事务,除非执行了dispose或close命令,否则该内存将保持不变。 但是如果在交易过程中发生错误,那么处理命令将被终止,除非它在try.. finally..
块内。
从finally
的意义上说,catch是devise给你的方式来处理/pipe理或解释它自己的错误。 把它想成是告诉你的人“嘿,我抓到了一些坏人,你想让我对他们做些什么?” 而finally
目的是确保您的资源被正确放置。 想一想,不pipe有没有坏人,他都会确保你的财产安全。
而且你应该让这两个人一起工作。
例如:
try { StreamReader reader=new StreamReader("myfile.txt"); //do other stuff } catch(Exception ex){ // Create log, or show notification generic.Createlog("Error", ex.message); } finally // Will execute despite any exception { reader.Close(); }