C#中的"using"块是用来做什么的?如何在C#中使用"using"块?

34

我看到在大多数示例中 SqlCommand 被使用如下

using (SqlConnection con = new SqlConnection(CNN_STRING))
{
    using (SqlCommand cmd = new SqlCommand("Select ID,Name From Person", con))
    {
        SqlDataAdapter da = new SqlDataAdapter(cmd);
        DataSet ds = new DataSet();
        da.Fill(ds);           
        return ds;
    }
}

我知道我们为什么要使用using语句。但是SqlCommand没有包含Close()方法,所以我们真的应该在using语句中使用它吗?


2
在这个例子中,你实际上不需要关心关闭连接,因为 Fill() 方法会自己管理连接,即使你没有使用 using 语句。 - Ankush Madankar
2
是的,你说得对。来自 MSDN 的说明:“与 SELECT 语句相关联的连接对象必须有效,但不需要打开。如果在调用 Fill 方法之前关闭了连接,则会打开该连接以检索数据,然后再关闭。如果在调用 Fill 方法之前已经打开了连接,则该连接保持打开状态。”- http://msdn.microsoft.com/en-us/library/377a8x4t.aspx - Omer
4个回答

42
因为它还实现了 IDisposable 接口。
Using 语句的目的是,当控制流到达 Using 语句末尾时,它将处理 Using 块中的对象并释放内存。它的目的不仅仅是自动关闭连接,基本上它将处置连接对象,并且显然连接也因此关闭。
它的目的是释放我们在 Using 语句中使用的资源。
根据 MSDN: 作为规则,当您使用一个 IDisposable 对象时,应该在 using 语句中声明和实例化它。using 语句以正确的方式调用对象的 Dispose 方法,并且(如前面所示)在 Dispose 被调用后也会导致对象本身立即超出范围。在 using 块内,对象是只读的,不能修改或重新分配。 using 语句确保即使在调用对象方法时发生异常,也会调用 Dispose。您可以通过将对象放在 try 块中,然后在 finally 块中调用 Dispose 来实现相同的结果;事实上,这就是编译器如何将 using 语句转换的方式。代码示例在编译时扩展为以下代码(注意额外的花括号以创建对象的有限范围):
您可以实例化资源对象,然后将变量传递给using语句,但这并不是最佳实践。在这种情况下,即使控制离开using块,该对象仍然在作用域内,尽管它可能不再访问其非托管资源。换句话说,它将不再完全初始化。如果您尝试在using块之外使用该对象,则有可能导致抛出异常。因此,通常最好在using语句中实例化对象并将其范围限制为using块。

1
如何判断一个类是否实现了IDisposable接口? - Omer
一个没有实现IDisposable接口的类不能使用using语句,必须实现IDisposable接口。 - Ehsan Sajjad
1
我的问题是我们如何知道SqlCommand实现了Idisposible接口?我是新手,所以想学习。 - Omer
2
你可以检查一个对象是否实现了IDisposable接口,如果是,则可以使用Dispose()方法,否则不可以。 - Ehsan Sajjad
1
如果你看到一个实现了IDisposable接口的类对象,它可以访问Dispose方法。请注意,这是关于编程的内容。 - Ehsan Sajjad

8

SqlCommand 实现了 IDisposable 接口,这意味着一个 using 语句在结束时会调用 .Dispose() 方法。虽然我不清楚 SqlCommand.Dispose() 做了什么,但是在使用完实例后调用 .Dispose() 是个好习惯,它可以清理数据库连接等资源。


SqlCommand.Dispose() 的目的是用于释放 SqlCommand 使用的所有资源。在这种情况下,对象 cmd。 - user3383479
如何判断一个类是否实现了IDisposable接口? - Omer
4
不同的方法之一是在Visual Studio中找到实例的类型,右键点击它,选择“转到定义”。这个窗口会告诉你这个类型是否实现了IDisposable - Jason Evans
2
现在源代码已经可用,你可以看到它的功能。虽然不是很多,但确实有一些:https://referencesource.microsoft.com/#System.Data/System/Data/SqlClient/SqlCommand.cs,53ad9885e5a8fc48 - Gabriel Luci
如果使用Jason的技巧,右键单击SqlCommand,转到定义,并查看public sealed class SQLiteCommand:DbCommand,ICloneable,因此它继承自DbCommand。右键单击DbCommand,转到定义,并查看它继承自IDisposable。 - John Doe

0

我进行了一些研究,并找到了一个答案,下面做了总结。如需更详细的解释,请参见这里

SqlCommandusing语句并没有处理太多内容。 SqlCommand间接继承自实现终结器的Component。除非实现已更改,否则GC会忽略第一次遇到超出范围的具有终结器的对象。 GC不会将其从内存中删除,而是调用终结器然后继续前进。第二次遇到它时,GC将从内存中删除该对象。这显然相当昂贵,因此您不希望终结器运行得太频繁。

为防止终结器运行,《组件》(SqlCommand间接继承自其中)实现了一个dispose方法,告诉GC不要调用终结器,因为dispose方法已经处理了任何清理工作。

简言之,using语句用于SqlCommand并没有什么特殊作用,但它可以避免调用更加昂贵的终结器。
如果您对此如何工作有更多细节,请在评论中告诉我。

0

SqlCommand.Dispose() 用于释放 SqlCommand 使用的所有资源。


你的回答可以通过提供更多支持信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的答案是正确的。您可以在帮助中心中找到有关如何编写良好答案的更多信息。 - Community

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