让我们在这里处理显式和隐式问题。
问:首先,当你完成一个局部变量时,将 null 分配给它有什么意义吗?
答:不,根本没有。当使用优化编译并且不在调试器下运行时,JITter 知道变量正在使用的代码段,并且当您通过该段时将自动停止将其视为根。换句话说,如果你给一个变量赋值,然后在某个时候不再读取它,即使你没有明确地将它设置为null,它也可能被收集。
所以你的例子可以安全地写成:
public void func1()
{
object obj = new object();
// implied more code here
}
如果“此处隐含更多代码”中没有任何代码访问过obj 变量,则不再将其视为根。
请注意,如果在未优化的程序集中运行,或者如果您将调试器连接到进程,这会发生变化。在这种情况下,变量的范围会被人为地扩展至其范围的末尾,以便于调试。
问:其次,周边类的字段呢?
答:在这里绝对可以有所作为。
如果您的方法周围的对象在很长一段时间内保持活动状态,并且不再需要字段内容,那么是的,将字段设置为 null 将使其引用的旧对象符合条件收藏。
所以这段代码可能有优点:
public class SomeClass
{
private object obj;
public void func1()
{
try
{
obj=new object();
// implied more code here
}
finally
{
obj = null;
}
}
}
但是,你为什么要这样做呢?相反,您应该努力编写不依赖于周围状态的更简洁的代码。在上面的代码中,您应该重构“此处隐含更多代码”以在要使用的对象中传递,并删除全局字段。
显然,如果您不能这样做,那么可以,只要不再需要其对象引用,就将字段设置为 null 是个好主意。
有趣的实验,如果您在LINQPad 中运行以下代码并进行优化,您期望输出是什么?
void Main()
{
var s = new Scary();
s.Test();
}
public class Scary
{
public Scary()
{
Console.WriteLine(".ctor");
}
~Scary()
{
Console.WriteLine("finalizer");
}
public void Test()
{
Console.WriteLine("starting test");
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Console.WriteLine("ending test");
}
}
Answer(鼠标悬停在您认为已掌握时显示):
.ctor
开始测试
终结器
结束测试
解释:
由于实例方法的隐式this 参数从未在方法内部使用,因此即使该方法当前正在运行,也会收集该方法周围的对象。