使用statement和try-catch() - 最后重复?

时间:2009-09-02 20:36:57

标签: c# using try-catch-finally

using(...)语句是try {} finally {}的语法糖。

但是,如果我有一个如下所示的使用声明:

using (FileStream fs = File.Open(path))
{


}

现在我想抓住打开这个文件可能导致的异常(这是相当高风险的代码,因为它可能因环境而失败),但是如果我在里面写try-catch会不会重复?当代码被编译成IL时,我假设当代码被JITted时,重复将被删除?

但是,我想要捕获打开文件可能导致的异常(所以我应该将try-catch包装在using语句的范围之外),以及我在using block中做的任何异常,所以我应该添加在街区内试试。

这似乎是我在CLR内部可能做的事情中添加了很多重复。 CLR是否添加了catch子句?

我的同事认为使用声明很混乱(但这是因为由于我需要对它们进行硬编码,所以单行很长,因为我需要非常快速地更改它们并且无法访问代码库的其他部分)。说同事不使用using语句,但是using语句和try-finally / try-catch-finally之间是否存在任何功能差异?我确实看到了一个这样的案例,其中WCF服务有一个关于使用finally和返回值的一个鲜为人知的角落案例(最后的事情)。解决方案是使用检查块。 C#中有这样的东西吗?

另一方面,是否所有类型都实现了非托管资源的IDisposale所有者?与我的朋友的讨论指出了不是的答案。 (我还在本论坛的使用部分阅读了一些主题,其中有一些非常好的知识)。

5 个答案:

答案 0 :(得分:7)

我的偏好是保留using语句并将其包装在try / catch中。外部try / catch将捕获您需要注意的任何异常,同时忽略Dispose()。如果你稍后重构它以将try / catch移动到其他地方(比如在调用函数中),这还有一个额外的好处就是保护你自己。

关于IDisposable的问题:任何人都可以出于任何理由实现这一点。没有技术理由将其限制为非托管资源。 (是否仅限于 代码中的非托管资源是一个不同的问题)。

答案 1 :(得分:7)

如果您真的需要处理某些异常,可以自己实现该模式。就个人而言,我仍然觉得在try / catch块中包装使用更简单(更重要的是,更清楚)。

但如果你自己做,请确保你做对了。 Using块还会创建一个匿名范围块,以便您的变量更快地符合收集条件。在.Dispose()块末尾调用的Using方法仅清除非托管资源,因此您的对象所拥有的任何内存可能会稍微停留一段时间。这不太可能是一个巨大的问题,但值得记住以防万一。

因此,要直接调整模式,您的代码需要看起来更像这样:

{
    FileStream fs;
    try
    {
        fs = File.Open(path);

    }
    catch (FileNotFoundException e) { /* ... */ }
    catch (IOException e) { /* ... */ }
    catch (Exception e) {/* ... */}
    finally
    {
        if (fs != null) fs.Dispose();
    }
}

就个人而言,我希望Using扩展为支持CatchFinally块。由于他们已经对代码进行了转换,因此似乎不会增加额外的复杂性。

答案 2 :(得分:4)

如果您需要明确处理在语句中可能发生的不同异常情况,您可以将using替换为try/catch/finally并在finally中明确调用Dispose()。或者您可以在try/catch区块周围放置一个using,以将特殊情况与确保处置区分开来。

事物using确实会确保调用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
}

答案 3 :(得分:4)

using和try-finally之间的最大区别是using只会调用Dispose()方法。如果您实现自己的finally块可以执行其他逻辑,例如日志记录,这将不包括在使用中。

答案 4 :(得分:3)

using构造是try/finally块的简写。也就是说,编译器生成:

FileStream fs = null;
try
{
    fs = File.Open(path);
    // ...
}
finally
{
    if (fs != null)
        fs.Dispose();
}

因此,如果您不需要using,则使用catch是正确的,但如果您这样做,则应该使用正常的try/catch/finally