使用语句和IDisposable接口

4

在 using 语句中声明的变量是否会一起被释放,因为它们在 using 块的范围内?

我需要这样做吗:

using (SomeIdisposableImplementation foo = new SomeIdisposableImplementation())
{
    using(SomeIdisposableImplementation2 bar = new SomeIdisposableImplementation2())
    {

    }
}

这样就足够了吗?"bar"和"foo"一起被处理了吗?
using (SomeIdisposableImplementation foo = new SomeIdisposableImplementation())
{
    SomeIdisposableImplementation2 bar = new SomeIdisposableImplementation2();
}

你在 SomeIdisposableImplementation2 中漏掉了 = 吗? - Praveen
3个回答

11

或者这样就够了,"bar"和"foo"一起处理?

不,"bar"不会被处理。

using语句转换为try-finally块,因此即使发生异常,finally块也会确保调用Dispose方法。

接下来是:

using (SomeIdisposableImplementation foo = new SomeIdisposableImplementation())
{
    SomeIdisposableImplementation2 bar = new SomeIdisposableImplementation2();
}

翻译成

{
    SomeIdisposableImplementation foo;
    try
    {
        foo = new SomeIdisposableImplementation();
        SomeIdisposableImplementation2 bar = new SomeIdisposableImplementation2();
    }
    finally
    {
        if (foo != null)
            foo.Dispose();
    }
}

未对bar进行处理而导致泄漏。


4
为了使用using语句同时释放它们,您不必将它们嵌套,而是可以这样写:
using (SomeIdisposableImplementation foo = new SomeIdisposableImplementation())
{
    using(SomeIdisposableImplementation2 bar = new SomeIdisposableImplementation2())
    {

    }
}

as

using (SomeIdisposableImplementation foo = new SomeIdisposableImplementation())
using(SomeIdisposableImplementation2 bar = new SomeIdisposableImplementation2())
{


}

1
在第二个版本中,bar 只会超出作用域但不会被处理。但是您可以将 foo 和 bar 放入同一个 using 命令中:
using (SomeIdisposableImplementation foo = new SomeIdisposableImplementation(), SomeIdisposableImplementation2 bar = new SomeIdisposableImplementation2())
{
    // use foo and bar
}

你也可以将变量直接放入using命令中:
SomeIdisposableImplementation foo = new SomeIdisposableImplementation();
SomeIdisposableImplementation2 bar = new SomeIdisposableImplementation2();
using (foo, bar)
{
    // use foo and bar
}

3
正确的语法是 using(S1 foo = new S1()) using (S2 bar = new S2()) { ... } ,或者如果 foobar 是相同类型的,则为 using(S1 foo = new S1(), bar = new S1()) { ... }。如果 foobar 是不同类型的,则不能使用 using(S1 foo = new S1(); S2 bar = new S2()) { ... } 的语法,尽管这样会更好。 - Eric Lippert
2
@ChrisSinclair:在面对线程中止异常时,using并不是完全健壮的。请考虑使用以下代码:S1 foo = null; try { foo = new S1(); ... } finally { if (foo != null) foo.Dispose(); }。现在假设在线程构造函数期间抛出线程中止异常,但是在构造函数创建非托管资源之后。foo将永远不会被分配,因此在finally中它仍然为null。在这里释放资源的唯一方法是在终结器中。这就是为什么您需要始终编写终结器以在部分构造对象面前具有健壮性的原因。 - Eric Lippert
@EricLippert 您是正确的,只有在两者类型相同时才可能实现,我搞混了。抱歉。 - Roland Bär
当然,Eric,但这是实现良好的一次性/非托管类型的一部分(正如你所指出的那样)。我只是指出Roland的第二个示例失败的原因。编辑:啊,第二次阅读时,我可以看到我的评论可能会暗示using块将正确管理此操作,而不是我想要的。 :) - Chris Sinclair
1
@ChrisSinclair:没错,我的观点是,虽然指出这里显示的代码在面对线程中止异常时不够健壮是正确的,但这本身并不是使用using语句的动机,因为using语句也无法抵御线程中止。总的来说,在异常情况下我真的很少关心清理资源,因为它是异常的。你根本无法在所有异常情况下清理资源,并且按定义它们是异常的;相反,应该担心在典型情况下始终进行清理。 - Eric Lippert
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接