C# 使用语句并尝试-catch()-最终重复?
using(…)语句是try{}finally{}的语法糖 但如果我有一个使用语句,如下所示:C# 使用语句并尝试-catch()-最终重复?,c#,using,try-catch-finally,C#,Using,Try Catch Finally,using(…)语句是try{}finally{}的语法糖 但如果我有一个使用语句,如下所示: using (FileStream fs = File.Open(path)) { } 现在,我想捕获打开此文件可能导致的异常(这是一个相当高的风险代码,因为它可能会由于环境而失败),但是如果我在内部编写try-catch,这不是重复吗?当代码被编译成IL时,我假设当代码被JIT时,重复会被删除 但是,我希望捕获打开文件可能导致的异常(因此我应该将try-catch包装在using语句的范围之外
using (FileStream fs = File.Open(path))
{
}
现在,我想捕获打开此文件可能导致的异常(这是一个相当高的风险代码,因为它可能会由于环境而失败),但是如果我在内部编写try-catch,这不是重复吗?当代码被编译成IL时,我假设当代码被JIT时,重复会被删除
但是,我希望捕获打开文件可能导致的异常(因此我应该将try-catch包装在using语句的范围之外),以及在using块内执行的任何操作的异常,以便在块内添加try-catch
这似乎是我在重复CLR内部可能做的事情。CLR是否添加catch子句
我的同事认为using语句很混乱(但这是因为由于我对它们进行了硬编码,所以一行稍长,因为我需要快速更改它们,并且无法访问代码库的其他部分)。上述同事没有使用using语句,但是using语句和try finally/try catch finally之间是否存在功能上的差异?我确实看到过这样的一个案例,其中WCF服务有一个关于使用finally和返回值(关于finally)的不为人知的例子。解决方法是使用检查块。在C#中有类似的东西吗
另一方面,实现IDisposale的所有类型都是非托管资源的所有者吗?与我朋友的讨论表明答案是否定的。(我还阅读了本论坛使用部分的一些帖子,其中有一些非常好的知识)。如果您需要明确处理声明期间可能发生的不同例外情况,您可以用
try/catch/finally
替换using
,并在finally中显式调用Dispose()
。或者您可以使用块在周围放置一个try/catch
,将异常情况与确保处置分开
使用
所做的唯一一件事就是确保调用Dispose(),即使在块内抛出异常也是如此。这是一个非常有限的、非常具体的通用try/[catch]/finally
结构的实现
重要的是,这些选项都没有任何真正的影响——只要它符合你的需要,可读性和可理解性,谁在乎呢?这不像是额外的尝试会成为瓶颈或任何事情
回答您的最后一个问题-不,IDisposable肯定不一定意味着实现者拥有非托管资源的句柄。这是一个更简单、更通用的模式。下面是一个有用的例子:
public class Timer : IDisposable
{
internal Stopwatch _stopwatch;
public Timer()
{
this._stopwatch = new Stopwatch();
this._stopwatch.Start();
}
public void Dispose()
{
this._stopwatch.Stop();
}
}
我们可以使用它来计时,而不必通过使用以下命令显式地依赖于启动和停止调用:
using(Timer timer = new Timer())
{
//do stuff
}
using和try finally之间最大的区别在于using只调用Dispose()
方法。如果您实现自己的finally块,则可以执行其他逻辑,例如日志记录,这不会包含在using中。我的首选是保留using语句并将其包装在try/catch中。外部try/catch将捕获您需要监视的任何异常,同时忽略Dispose()。如果您以后重构它以将try/catch移到其他位置(如在调用函数中),那么这还有一个额外的优点,可以保护您免受攻击
至于你关于IDisposable的问题:任何人都可以出于任何他们喜欢的原因实现它。没有技术上的理由将其局限于非托管资源。(是否应将其限制在代码中的非托管资源是另一个问题)。使用的构造是try/finally
块的缩写。也就是说,编译器生成:
FileStream fs = null;
try
{
fs = File.Open(path);
// ...
}
finally
{
if (fs != null)
fs.Dispose();
}
因此,如果您不需要捕获
,则可以使用使用
,但如果您需要捕获
,则应使用普通的尝试/catch/finally
,如果您确实需要处理某些异常,则可以自己实现该模式。就我个人而言,我仍然觉得在try/catch块中包装using更简单(更重要的是更清晰)
但是如果你自己做的话,确保你做对了。Using
块还会创建一个匿名作用域块,以便您的变量能够更快地被收集。使用
块在末尾调用的.Dispose()
方法只会清理非托管资源,因此对象持有的任何内存可能会挂起更长的时间。这不太可能是个大问题,但值得记住,以防万一
因此,要直接适应模式,您的代码需要如下所示:
{
FileStream fs;
try
{
fs = File.Open(path);
}
catch (FileNotFoundException e) { /* ... */ }
catch (IOException e) { /* ... */ }
catch (Exception e) {/* ... */}
finally
{
if (fs != null) fs.Dispose();
}
}
就我个人而言,我希望看到使用
扩展到支持Catch
和Finally
块。因为他们已经在代码上执行转换,所以这似乎不会增加那么多额外的复杂性。在try
/catch
中使用本身包装没有什么错。它比finally
更清晰,因为在块退出后,不必查看finally
就可以立即看到释放了哪些资源。不过,使用finally支持吗?您是在哪里发现使用匿名作用域块的?我想知道更多关于这方面的情况。因此,当我在using块中打开一个文件(例如filestream.open())时,就会出现这个异常。如果using语句实现了try/finally,我必须将其包装在try/catch中,以获得捕获。