我们是否需要将自定义 class 的对象设置为 null,否则 GC 会处理它

Do we need to set object of custom class as null or GC will take care of it

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

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?
                }
            }
        }
    }
}

用法

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

另一个class

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?

TL;DR 您在 SqlReader 处缺少 using 语句。其他场景由 GC 处理。如果您完成了实现 IDisposable 接口的对象,请始终使用 Disposeusing(最后一条注释中有一些例外)。

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

一般来说,所有没有未完成引用的托管对象都将由 GC 处理。所以一般来说,你不需要显式地将它们设置为空。

但是,如果一个对象实现了 IDisposable 接口,当您使用完该对象时,最好调用 Dispose。尽管(如果正确实施),这也会在 Finalize 调用期间调用,但最好显式调用 Dispose(或通过 using 语句)。数据库连接、图形对象或网络连接等对象可能会导致连接池已满或占用图形内存。

请注意,这都是关于范围和未完成的引用。

如果你有这样的方法,object "behind" 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;
}

但如果包含对象是静态的或以其他方式保持活动状态,您就会遇到这种情况;只要在某处存在对 that FooClass object:

的有效引用,database 就会保持活动状态
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 调用。