带有活动事件处理程序的未引用对象的垃圾收集

Hap*_*mad 2 .net c#

在以下程序中......

using System;

class Program
{
    static Parent parent;

    static void Main( string[] args )
    {
        parent = new Parent();
        // The program hereafter runs for a long time and occasionally
        // causes parent.SomeEvent to be raised.
    }
}

class Parent
{
    public event EventHandler SomeEvent;

    public Parent()
    {
        new Handler( this );
    }
}

class Handler
{
    public Handler( Parent parent )
    {
        parent.SomeEvent += parent_SomeEvent;
    }

    void parent_SomeEvent( object sender, EventArgs e )
    {
        // Does something important here.
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意Handler,尽管已订阅,但未引用实例化对象SomeEvent.是否有可能在程序运行一段时间后,垃圾收集器可能决定消除该Handler实例,parent_SomeEvent因此无论何时parent.SomeEvent引发都不再调用其处理程序?

我需要对我正在撰写的应用程序进行澄清.Handler如上所示,有许多类似的对象被实例化,没有被引用.Handler订阅的主要目的是订阅SomeEvent.没有有用的方法来调用对Handler实例的引用,所以我可以不参考它.调试时我没有遇到任何问题.但是现在我担心在应用程序长时间运行并且垃圾收集器更活跃时部署后可能出现问题.

bas*_*bas 5

Handler删除对该对象的所有引用之前,不会对类的实例化对象进行垃圾回收.

因此,在您取消订阅所有事件处理程序之前,该对象将存在.因为订阅的事件处理程序也是将Parent实例连接到实例的另一个"引用" Handler.

是否有可能在程序运行一段时间后,垃圾收集器可能决定取消Handler实例,因此每当引发parent.SomeEvent时都不再调用其parent_SomeEvent处理程序?

这正是GC只从堆中收集"未引用"对象的原因.您的方案将导致未定义的NullReferenceExceptions,完全取决于GC何时决定删除对象.幸运的是,事实并非如此:).

此外,GC足够智能,可以确定未引用对象(未引用的岛)的孤立池.因此,在场景中,您的父对象也会被取消引用,而GC将确定整个对象链是未引用的(Parent对象,event订阅和handler对象),并将在下一个收集周期中一起收集它们.

如果可以的话,我会推荐这篇MSDN文章.为您提供.NET中垃圾收集的广泛概念的概述.在编码时要记住非常有用.