使用语句和try-catch()-finally重复?
using(...) 语句是 try{} finally {} 的语法糖。
但是,如果我有如下所示的 using 语句:
using (FileStream fs = File.Open(path))
{
}
现在我想捕获打开此文件可能导致的异常(这是相当高风险的代码,因为它可能会因环境而失败),但是如果我编写 try-catch里面岂不是重复了?当代码被编译为 IL 时,我假设当代码被 JITted 时重复会被删除?
但是,我想捕获打开文件可能导致的异常(因此我应该将 try-catch 包装在 using 语句的范围之外),以及捕获在 using 块内执行的任何操作的异常,因此我应该添加 try-catch块内。
这似乎是我在 CLR 内部可能执行的操作中添加了很多重复内容。 CLR 是否添加了 catch 子句?
我的同事认为 using 语句很混乱(但这是因为单行稍微长一些,因为我对它们进行了硬编码,因为我需要非常快速地更改它们并且无法访问代码库的其他部分)。这位同事没有使用 using 语句,但是 using 语句和 try-finally/try-catch-finally 之间有任何功能差异吗?我确实看到过这样的一个案例,其中 WCF 服务有一个关于使用finally 和返回值(关于finally 的东西)的不为人所知的极端情况。解决方案是使用检查块。 C#中有类似的东西吗?
另一方面,是否所有实现 IDisposale 的类型都是非托管资源的所有者?与我朋友的讨论表明答案是否定的。 (我还阅读了该论坛的使用部分中的一些帖子,那里有一些非常好的知识)。
The using(...) statement is syntactic sugar for try{} finally {}.
But if I then have a using statement like below:
using (FileStream fs = File.Open(path))
{
}
Now I want to catch the exceptions that opening this file could cause (and this is fairly high risk code in that it can fail due to the environment), but if I write try-catch inside would that not be repetition? When the code gets compiled to IL, I assume the repetition will get deleted when the code is JITted?
However, I would want to catch the exceptions opening a file can cause (so I should wrap the try-catch outside the using statement's scope), and also the exceptions for whatever I do inside the using block so I should add the try-catch inside the block.
This seems like I am adding a lot of repetition to what the CLR probably does inside. Does the CLR add catch clauses?
My colleague argued that a using statement is messy (but this was because a single line was slightly long due to me hard coding them as I needed to change them very quickly and didn't have access to other parts of the code base). Said colleague doesn't use the using statement but is there ever any functional difference between the using statement and try-finally/try-catch-finally? I did see one case of this where WCF services have a not well known corner case about using finally and returning values (something about finally). The solution was to use a check block. Is there anything like this in C#?
On another note, are all types which implement IDisposale owners of unmanaged resources? A discussion with my friend pointed the answer to being no. (I have also read some threads in the using section of this forum, some very good knowledge there).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
如果您确实需要处理一些异常,您可以自己实现该模式。就我个人而言,我仍然发现将 using 包装在 try/catch 块中更简单(更重要的是,更清晰)。
但如果你自己做,请确保你做对了。
Using
块还会创建一个匿名作用域块,以便您的变量更快地符合收集条件。在Using
块末尾调用的.Dispose()
方法仅清理非托管资源,因此您的对象持有的任何内存都可能会被清除。多呆一会儿。这可能不是一个大问题,但值得记住以防万一。因此,要直接适应模式,您的代码需要看起来更像这样:
就个人而言,我希望看到
Using
扩展到支持Catch
和最后
块。由于他们已经对代码执行了转换,因此这似乎不会增加太多额外的复杂性。You can implement the pattern yourself if you need to handle some exceptions if you really want to. Personally, I still find it simpler (and more importantly, clearer) to just wrap the using in try/catch block.
But if you do it yourself, make sure you get it right. The
Using
block also creates an anonymous scope block so that your variable becomes eligible for collection sooner. The.Dispose()
method that's called at the end of theUsing
block only cleans up unmanaged resources, and so any memory your object holds may hang around a little longer. It's not likely a huge concern, but it is worth remembering just in case.So, to do a direct adaption of the pattern, your code needs to look more like this:
Personally, I'd like to see
Using
expanded to supportCatch
andFinally
blocks. Since they're already performing a transformation on the code, it doesn't seem like this would add that much additional complexity.我的偏好是保留 using 语句并将其包装在 try/catch 中。外部的 try/catch 将捕获您需要监视的任何异常,同时忽略 Dispose()。如果您稍后重构此代码以将 try/catch 移到其他地方(例如在调用函数中),这还有一个额外的优点,可以保护您免受自己的伤害。
至于你关于 IDisposable 的问题:任何人都可以以任何他们喜欢的理由来实现它。没有任何技术原因将其限制为非托管资源。 (是否应该仅限于您的代码中的非托管资源是另一个问题)。
My preference would be to keep the using statement and wrap it in a try/catch. The outer try/catch will catch whatever exceptions you need to watch for, while ignoring the Dispose(). This has the added advantage of protecting you from yourself should you refactor this later to move the try/catch elsewhere (like in a calling function).
As far as your question about IDisposable: Anyone can implement this for any reason they like. There is no technical reason for it to be limited to unmanaged resources. (Whether or not it should be limited to unmanaged resources in your code is a different question).
using 和 try-finally 最大的区别是 using 只会调用
Dispose()
方法。如果您实现自己的finally块可以执行其他逻辑,例如日志记录,这不会包含在using中。The biggest difference between using and try-finally is that using will only call the
Dispose()
method. If you implement your own finally block can do other logic, for example logging, which would not be included in the using.如果您需要显式处理语句中可能发生的不同异常情况,可以将
using
替换为try/catch/finally
并调用Dispose() 明确地放在finally 中。或者,您可以在
using
块周围放置一个try/catch
,以将异常情况与确保处置分开。using 所做的唯一事情是确保即使在块内抛出异常,也会调用 Dispose() 。这是一般
try/[catch]/finally
结构的非常有限、高度具体的实现。重要的是,这些选项都没有任何实际影响 - 只要它满足您的需要,可读且易于理解,谁在乎呢?额外的尝试不会成为瓶颈或其他什么!
回答你的最后一个问题 - 不,IDisposable 绝对并不一定意味着实现者拥有非托管资源的句柄。这是一个比这更简单、更通用的模式。这是一个有用的示例:
我们可以使用它来计时,而不必显式依赖 start 和 stop 被调用,方法是:
If you need to explicitly handle different exceptional cases which may happen during the statement, you could replace
using
withtry/catch/finally
and callDispose()
explicitly in the finally. Or you could put atry/catch
around theusing
block to separate exceptional circumstances from ensuring disposal.The only thing
using
does is ensure Dispose() is called even if an exception is thrown inside the block. That is a very limited, highly specific implementation of the generaltry/[catch]/finally
structure.The important thing is that none of these options have any real impact - as long as it does what you need, is readable and understandable, who cares? It's not like an extra try will be a bottleneck or anything!
To answer your last question - no, IDisposable definitely does not necessarily mean the implementer has handles to unmanaged resources. It's a far simpler and more generic pattern than that. Here's one useful example:
We can use this to time things without having to explicitly rely on start and stop being called by using:
using
构造是try/finally
块的简写。也就是说,编译器生成:因此,如果您不需要
catch
,则使用using
是正确的,但如果需要,则应该使用普通的尝试/捕获/最后
。The
using
construct is shorthand for atry/finally
block. That is, the compiler generates:So it is proper to use
using
if you do not need acatch
, but if you do then you should just use a normaltry/catch/finally
.