Lambda Expression导致弱引用的目标不能是GC吗?

2 .net lambda weak-references

namespace Test
{
    class Test
    {
        delegate void HandleMessage(string message);

        public void handleMessage(string message){}

        static void Main(string[] args)
        {
            HandleMessage listener1 = new Test().handleMessage;
            WeakReference w1 = new WeakReference(listener1);

            HandleMessage listener2 = (message) => { };
            WeakReference w2 = new WeakReference(listener2);

            Console.WriteLine("w1.Target:\t[" + w1.Target + "]");
            Console.WriteLine("w2.Target:\t[" + w2.Target + "]");

            listener1 = null;
            listener2 = null;
            GC.Collect();
            Console.WriteLine("after GC");

            Console.WriteLine("w1.Target:\t[" + w1.Target + "]");
            Console.WriteLine("w2.Target:\t[" + w2.Target + "]");

            Console.ReadLine();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

GC后为什么w2.Target不为空?

    w1.Target:      [Test.Test+HandleMessage]
    w2.Target:      [Test.Test+HandleMessage]
    after GC
    w1.Target:      []
    w2.Target:      [Test.Test+HandleMessage]

编辑

感谢所有答案,Brian Rasmussen和Jon Skeet你的答案是正确的.现在我彻底了解发生了什么,所以我写了另一个例子来使一切更清楚.

以下示例显示:

如果Test#create()没有引用任何实例属性或方法,那么编译器将创建"private static HandleMessage CS $ <> 9__CachedAnonymousMethodDelegate1",就像Jon Skeet所说的那样 - 当你使用它时它会更有效率lambda表达多次.

如果Test#create()确实引用了实例属性或方法,就像下面调用this.ToString(); 然后编译器无法创建静态方法来替换intstance方法的逻辑,因此在GC之后,可以收集HandleMessage实例.

namespace Test
{
    class Test
    {
        public delegate void HandleMessage(string message);

        public void handleMessage(string message)
        {
        }

        public HandleMessage create()
        {
            return (message) => { 
                //this.ToString(); 
            };
        }       

        static void Main(string[] args)
        {
            HandleMessage listener1 = new Test().handleMessage;
            WeakReference w1 = new WeakReference(listener1);

            HandleMessage listener2 = new Test().create();//(message) => { };
            WeakReference w2 = new WeakReference(listener2);

            Console.WriteLine("w1.Target:\t[" + w1.Target + "]");
            Console.WriteLine("w2.Target:\t[" + w2.Target + "]");

            listener1 = null;
            listener2 = null;
            GC.Collect();
            Console.WriteLine("after GC");

            Console.WriteLine("w1.Target:\t[" + w1.Target + "]");
            Console.WriteLine("w2.Target:\t[" + w2.Target + "]");

            Console.ReadLine();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Bri*_*sen 6

它与lambdas无关.匿名委托可以观察到相同的行为.因此,如果您更改为代码

HandleMessage listener2 = delegate(string message) => { };
Run Code Online (Sandbox Code Playgroud)

你得到相同的结果.

在第一种情况下,您在Test实例上有一个实例方法.由于在无效时没有对此实例的其他引用listener1,因此可能会收集它.

在第二种情况下,匿名方法必须放在某种类型上(因为方法本身不能存在).在这种情况下,编译器将匿名方法作为静态方法放在您的Test类上.此外,引用存储在Test类型的静态成员中.因此Type对该方法也有静态引用,这就是它在集合中存活的原因.

看一下IL,了解事情是如何连线的.