使用use来处理嵌套对象

时间:2010-04-20 18:32:51

标签: c# .net garbage-collection idisposable

如果我有像这样的嵌套对象的代码,我是否需要使用嵌套的using语句来确保SQLCommand和SQLConnection对象都正确处理,如下所示,或者如果实例化SQLCommand的代码我还可以在外部使用声明中。

using (var conn = new SqlConnection(sqlConnString))
{
    using (var cmd = new SqlCommand())
    {
        cmd.CommandType = CommandType.Text;
        cmd.CommandText = cmdTextHere;
        conn.Open();

        cmd.Connection = conn;
        rowsAffected = cmd.ExecuteNonQuery();
    }
}

4 个答案:

答案 0 :(得分:8)

是。你可以把它清理一下这个

using (SqlConnection conn = new SqlConnection(sqlConnString))
using (System.Data.SqlClient.SqlCommand cmd = new System.Data.SqlClient.SqlCommand())
{
   // code here
}

但是你仍然希望每个IDisposable对象都有一个using

修改:使用内部using语句考虑 not 的示例。

class A : IDisposable
{
    public void Dispose()
    {
        Console.WriteLine("A Disposed");
    }
}

class B : IDisposable
{
    public void Dispose()
    {
        Console.WriteLine("B Disposed");
    }
}

代码

using (A a = new A())            
{
    B b = new B();
}

在区块结束时,A被妥善处理。 B怎么了?嗯,它超出范围,只是等待垃圾收集。 B.Dispose()没有被调用。另一方面

using (A a = new A())
using (B b = new B())
{
}

当执行离开块时(实际上, blocks ),编译后的代码会执行对每个对象的Dispose()方法的调用。

答案 1 :(得分:4)

您应该同时调用dispose,但是,如果您这样做,则更容易阅读:

using (SqlConnection conn = new SqlConnection(sqlConnString)) 
using (SqlCommand cmd = new SqlCommand()) 
{ 
     cmd.CommandType = CommandType.Text; 
     cmd.CommandText = cmdTextHere; 
     conn.Open(); 

     cmd.Connection = conn; 
     rowsAffected = cmd.ExecuteNonQuery(); 
} 

因为创建了一个隐式块(比如if语句或for语句),并且因为使用了整个块,所以你不需要大括号,在我看来它看起来更整洁。有些人会说你总是使用大括号,因为很容易意外地添加第二个语句并创建一个bug,但在这种情况下我认为这不太可能。

答案 2 :(得分:4)

您可以省略SqlCommand周围的使用。 GC最终将为您清理它。但是,我强烈建议你不要这样做。我会解释原因。

SqlCommand间接继承自System.ComponentModel.Component,因此会继承其Finalizer方法。不在SqlCommand上调用dispose将确保命令在超出范围后至少提升一代(.NET垃圾收集器为generational gc)。例如:当命令在gen 1中时,它将继续到gen 2.可终结对象在内存中保留更长时间以确保终结器可以安全运行。但是不仅命令本身保存在内存中,而且它引用的所有对象都将它与它生成。它将引用的对象是SqlConnectionSqlParameter对象列表,可能很大的CommandText字符串,以及它引用的许多其他内部对象。只有在收集该代数时才能删除该内存,但生成的次数越多,扫描的次数就越少。

因此,不调用会给终结器线程带来额外的内存压力和额外的工作。

当.NET无法分配新内存时,CLR将强制所有代的垃圾收集。在此之后,运行时通常会有足够的空间来分配新对象。但是,当内存中有很多对象仍然必须被提升到下一代(因为它们可以最终化,或者被可终结对象引用)时,这种强制收集发生时,CLR可能无法释放足够的记忆力。这将是OutOfMemoryException的结果。

我必须承认我从未见过这种情况,因为开发人员并没有只处理他们的SqlCommand个对象。但是,由于没有正确处理对象,我在生产系统中看到了很多OOM。

我希望这给出了一些关于GC如何工作的背景以及没有正确处理(可终结)对象的风险。我总是丢弃所有一次性物品。虽然看看Reflector可以证明这不一定适用于某种类型,但这种编程会导致代码的可维护性降低,并使代码依赖于类型的内部行为(这种行为将来可能会发生变化)。

答案 3 :(得分:0)

要回答你的问题,是的,你可以这样做。

由于SqlCommand对象受外括号限制,因此当执行离开块时,GC将收集它。

其他答案也没问题,但他们没有完全回答你的问题:)