为什么使用对象初始化程序可以使对象保持活动状态

本文关键字:对象 活动状态 可以使 初始化 为什么 程序 | 更新日期: 2023-09-27 18:28:36

我最近看到了这篇SO文章,并根据我的场景对其进行了调整,如下所示:

using System;
using System.Collections.Generic;
namespace ConsoleApplication18
{
    class Program
    {
        static void Main(string[] args)
        {
            Manager mgr = new Manager();
            var obj = new byte[1024];
            var refContainer = new RefContainer();
            refContainer.Target = obj;
            obj = null;
            mgr["abc"] = refContainer.Target;
            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
            Console.WriteLine(mgr["abc"] != null); // true (still ref'd by "obj")
            refContainer = null;
            GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
            Console.WriteLine(mgr["abc"] != null); // false (no remaining refs)           
        }
    }
    class RefContainer
    {
        public object Target { get; set; }
    }
    class Manager
    {
        Dictionary<string, WeakReference> refs =
        new Dictionary<string, WeakReference>();
        public object this[string key]
        {
            get
            {
                WeakReference wr;
                if (refs.TryGetValue(key, out wr))
                {
                    if (wr.IsAlive)
                        return wr.Target;
                    refs.Remove(key);
                }
                return null;
            }
            set
            {
                refs[key] = new WeakReference(value);
            }
        }
    }
}

运行此程序会得到以下预期结果:

True
False
Press any key to continue . . .

然而,改变这个:

var refContainer = new RefContainer();
refContainer.Target = obj;

为此(使用对象初始化程序语法):

var refContainer = new RefContainer() { Target = obj };

给出以下输出:

True
True
Press any key to continue . . .

这是怎么回事?为什么仅仅因为使用了Object Initializer,引用的生存期就会不同?

为什么使用对象初始化程序可以使对象保持活动状态

为什么仅仅因为使用了Object Initializer,引用的生存期就会不同?

无论如何,我实际上无法重现您的问题,但我怀疑这是因为:

var refContainer = new RefContainer() { Target = obj };

相当于:

var tmp = new RefContainer();
tmp.Target = obj;
var refContainer = tmp;

因此,您最终得到了对堆栈上对象的额外引用。现在,当不在调试器下运行时,我希望GC注意到该堆栈位置不再被读取,并允许对对象进行垃圾收集-但当您在调试器下执行时,GC更为保守,我怀疑它将所有堆栈变量视为GC根。

不过,这只是一个猜测——无论如何,如果不能复制它,很难说是肯定的。

编辑:在非调试模式下,obj = null;refContainer = null;的分配毫无意义;因为变量在该点之后不会被读取,GC会将它们作为GC根忽略。