我们是否需要将自定义类的对象设置为null或GC将处理它

时间:2017-05-16 18:52:24

标签: c# class garbage-collection idisposable

请查看下面给出的示例代码。我有两个问题:
1.我们是否需要在返回数据之前关闭数据阅读器或GC会处理它? 2.我们是否需要将自定义类对象设置为null或GC将在这两种情况下处理它?<​​/ p>

public class Database
{
    public string DoSomething()
    {
        using (SqlConnection con = new SqlConnection(connectionString))
        {
            using (SqlCommand cmd = new SqlCommand("Select ID,Name From Person", con))
            {
                SqlDataReader reader = cmd.ExecuteReader();

                while(reader.Read())
                {
                    return reader["Name"].ToString();
                    //will this close reader once string is returned or we have to close it?
                }
            }
        }
    }
}

USAGE

Database database = new Database();
string res = database.DoSomething();
database = null; //is it necessary to do this or GC will take care of it?

另一个班级

public class XML
    {
        public void ReadXML()
        {
            XmlDocument doc = new XmlDocument();
            doc.Load("somefile.xml");
        }
    }

用法

XML xml = new XML();
xml.ReadXML();
xml = null; //is it necessary to do this or GC will take care of it?

1 个答案:

答案 0 :(得分:2)

<强> TL; DR 您在using错过了SqlReader声明。其他场景由GC处理。如果您已经完成了实现Dispose接口的对象,请始终使用usingIDisposable(在最后一个注释中有一些例外)。

注意:的 有关生命周期和范围的“副标题”差异,请参阅@ Damien_The_Unbeliever的评论。有关垃圾回收的详细信息,请参阅MSDN:https://msdn.microsoft.com/en-us/library/0xy59wtx(v=vs.110).aspx

通常,所有没有未完成引用的托管对象都将由GC处理。因此,通常,您不需要将它们显式设置为null。

但是,如果某个对象实现了IDisposable接口,则在完成该对象后最好调用Dispose。虽然(如果正确实现),也会在Finalize调用期间调用它,但最好是显式调用Dispose(或通过using语句调用)。数据库连接,图形对象或网络连接等对象可能会导致完整的连接池或占用图形内存。

请注意,这完全是关于范围和杰出的参考资料。

如果您有这样的方法,“object后面的database”会在离开范围后立即处理,因为没有其他变量引用此对象:

public void Foo()
{
   Database database = new Database();
   string res = database.DoSomething();
   //you don't need this call 
   // because the will be no outstanding references after the method exits.
   //database = null;
}

但是如果包含的对象是静态的或以另一种方式保持活着,那么你将得到这个场景;只要在那个 database对象的某处有一个有效的引用,FooClass就会保持活动状态:

public class FooClass
{
    Database database = new Database();

    public void Foo()
    {  
        //some operation
    }    
}

如果成员实现IDisposable,最好通过using语句或明确(Dispose())来调用它。虽然GC应该处理这个问题,特别是对于第三方库,但是要求IDisposable正确实现。如果你看到这个例子,你可以看到它可能很棘手,因此在遇到内存泄漏时是个嫌疑人。

Proper use of the IDisposable interface

所以,回答你问题的情景:

在下文中,using语句等同于try, catch finaly块。见https://docs.microsoft.com/en-us/dotnet/articles/csharp/language-reference/keywords/using-statement

public class Database
{
    public string DoSomething()
    {
        using (SqlConnection con = new SqlConnection(connectionString))
        {
            using (SqlCommand cmd = new SqlCommand("Select ID,Name From Person", con))
            {
                //note: using a using here as well
                using (SqlDataReader reader = cmd.ExecuteReader())
                {
                    while(reader.Read())
                    {
                        //this is ok, because the USING will take care of the disposure.
                        return reader["Name"].ToString();
                    }
                }
            }
        }
    }
}

到目前为止,我认为这是回答关于GC的问题的足够信息。但有一点需要注意;如果您正面对非托管对象,则需要进行其他正确处理。 COM例如需要Marshall.ReleaseComObject(),而某些库(我知道的一些工业相机驱动程序)需要明确的CleanUp调用。