所有一次性对象是否都在using块内被处理?

10

这是我过去经常问自己的问题,当我嵌套使用五层语句时。阅读文档,发现关于块内实例化的其他可处理对象的提及方式并没有明确说明,因此我认为将其记录在Stack Overflow归档中是一个好的问题。

考虑以下内容:

using (var conn = new SqlConnection())
{
    var conn2 = new SqlConnection();
}

// is conn2 disposed?
7个回答

15

不是所有的变量都会自动被处理。只有在`using`子句中明确列出的变量集才会被自动清理。


1
所以你需要嵌套6个using语句 :-) - Eric J.

6
显然我有答案... ;-)
答案是否定的。只有在使用声明中的对象才会被处理。
[Test]
public void TestUsing()
{
    bool innerDisposed = false;
    using (var conn = new SqlConnection())
    {
        var conn2 = new SqlConnection();
        conn2.Disposed += (sender, e) => { innerDisposed = true; };
    }

    Assert.False(innerDisposed); // not disposed
}

[Test]
public void TestUsing2()
{
    bool innerDisposed = false;
    using (SqlConnection conn = new SqlConnection(), conn2 = new SqlConnection())
    {
        conn2.Disposed += (sender, e) => { innerDisposed = true; };
    }
    Assert.True(innerDisposed); // disposed, of course
}

FYI:在我提交问题之前,我已经将答案复制到剪贴板中了。 - Sky Sanders
我认为在某种情况下,该线程可能会被暂停,以便SqlConnection.Dispose()会在conn2变量超出范围并且您的Assert行之间由Component.Finalizer()调用。这里存在竞争条件,不是吗? - binki

4
如果您想了解使用语句的确切规则,请参阅规范的第8.13节。所有问题都应在那里得到明确的回答。

@Eric,感谢您提供的参考。我发布这个问题是在通过我的答案中的测试来解决它,更多地是作为公共服务而不是其他任何事情。 - Sky Sanders
2
@Eric:能否提供一下规范的链接,给那些不知道如何进入MSDN的人? - John Saunders

2
不,它不起作用,conn2 不会被处理。请注意,多个using是唯一允许不使用括号以提高可读性的情况:
        using (var pen = new Pen(color, 1))
        using (var brush = new SolidBrush(color))
        using (var fontM60 = new Font("Arial", 15F, FontStyle.Bold, GraphicsUnit.Pixel))
        using (var fontM30 = new Font("Arial", 12F, FontStyle.Bold, GraphicsUnit.Pixel))
        using (var fontM15 = new Font("Arial", 12F, FontStyle.Regular, GraphicsUnit.Pixel))
        using (var fontM05 = new Font("Arial", 10F, FontStyle.Regular, GraphicsUnit.Pixel))
        using (var fontM01 = new Font("Arial", 8F, FontStyle.Regular, GraphicsUnit.Pixel))
        using (var stringFormat = new StringFormat())
        {
        }

这种方式,嵌套的using不是什么大问题。


1

不行。使用 using 语句会导致其中的对象被处理。如果你想让这两个对象都被处理,你应该将代码重写为:

using (var conn = new SqlConnection())
{
    using (var conn2 = new SqlConnection())
    {
        // use both connections here...
    }
}

或者,你也可以使用更简洁的语法:

using (SqlConnection conn = new SqlConnection(), conn2 = new SqlConnection())
{
    // use both connections here...
}

4
请注意,您可以更紧凑地编写此代码。"using(SqlConn conn1 = new SqlConn(), conn2 = new SqlConn()) { stuff }" 注:该代码段使用了C#语言中的using语法,用于自动关闭数据连接并释放资源,其中同时创建并使用了两个SqlConn对象(conn1和conn2)。 - Eric Lippert
此外,在 Visual Studio 中,使用 using 语句而不是块 (using(var conn=new SqlConnection())using(var conn2=new SqlConnection()){ ... }) 将被很好地对齐。它不会缩进第二个 using - Jeffrey L Whitledge
@Eric Lippert:我更新了以显示该选项... @Jeffrey:我倾向于不这样做,因为如果您不为每个语句使用大括号,StyleCop会抱怨。 - Reed Copsey

0
不是的。请使用ILDASM或Reflector检查生成的IL。

0
只有在 using() 内部声明的变量会被处理,而不是实际的代码块。

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