处理对象时是否需要将自定义事件设置为null?

时间:2010-01-05 22:13:46

标签: c# events dispose

Lets说我们有2个对象,Broadcaster和Listener。 Broadcaster有一个名为Broadcast的事件,Listener订阅了该事件。如果在没有取消订阅Broadcast事件的情况下处理Listener,它将被保留在内存中,因为Broadcaster包含的事件委托引用它。

我很好奇的是,如果Broadcaster在没有Listener取消订阅或Broadcaster设置的情况下处理,则Broadcast = null会将Broadcaster保留在内存中吗?

除了一位认为不将事件设置为null的博主将内容保存在内存中时(找到here),我无法找到任何对这个问题有硬答案的内容。

我想听听为什么或为什么没有解释。

感谢。

更新: Forum Thread where one developer indicates events should be set to null, but Jon Skeet indicates it's not necessary, but doesn't elaborate.

2 个答案:

答案 0 :(得分:9)

请注意,委托人不会让发布者保持活跃状态​​(他们只保持目标=订阅者活着),因此没有多少订阅(自己)会使广播公司保持活跃状态​​。因此,从这个角度来看,无论是否处置都无关紧要。如果没有引用广播公司的项目(并且事件订阅对此无关紧要),则它将有资格进行收集。

基本上,委托是MethodInfoobject个引用的一对(列表);要调用的方法,以及要调用的对象“arg0”(又名this)。它只是没有引用引发事件的对象。

以下证据表明听众保持源存活;您应该看到“Source 1”被收集,即使我们仍然有订阅的匹配侦听器。正如预期的那样,“听众2”收集,因为我们仍然有匹配的广播公司:

class DataSource
{
    public DataSource(string name) { this.name = name; }
    private readonly string name;
    ~DataSource() { Console.WriteLine("Collected: " + name); }

    public event EventHandler SomeEvent;
}
class DataListener
{
    public DataListener(string name) { this.name = name; }
    private readonly string name;
    ~DataListener() { Console.WriteLine("Collected: " + name); }
    public void Subscribe(DataSource source)
    {
        source.SomeEvent += SomeMethodOnThisObject;
    }
    private void SomeMethodOnThisObject(object sender, EventArgs args) { }
}

static class Program
{
    static void Main()
    {
        DataSource source1 = new DataSource("Source 1"),
                source2 = new DataSource("Source 2");
        DataListener listener1 = new DataListener("Listener 1"),
                listener2 = new DataListener("Listener 2");
        listener1.Subscribe(source1);
        listener2.Subscribe(source2);
        // now we'll release one source and one listener, and force a collect
        source1 = null;
        listener2 = null;
        GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
        GC.WaitForPendingFinalizers(); // source 1 gets collected, ONLY

        Console.WriteLine("Done");
        Console.ReadLine();
        GC.KeepAlive(source2); // prevents collection due to optimisation
        GC.KeepAlive(listener1); // prevents collection due to optimisation
    }
}

答案 1 :(得分:4)

没有。 Broadcast事件中的委托目标引用Listener对象。这将使Listener对象保持活动状态。 Listener对象没有任何对Broadcast对象的引用。

注意术语。处理Broadcast对象什么都不做。它必须是垃圾收集,只有在没有对象的引用时才会发生。当发生这种情况时,也将自动收集委托对象,因为对它的唯一引用是由私有事件委托对象维护的委托目标的内部列表。这也删除了委托对侦听器的引用。如果没有其他对侦听器的引用,它也将被收集。如果它仍然存在,它将不再获得事件通知。简而言之:您不必在Broadcast类中将事件显式设置为null。

在侦听器中不完全相同,它由订阅的事件引用。如果宣布它不适合业务(处置)但广播公司仍然有效,则应明确删除其事件订阅。 SystemEvents类是它的极端版本​​,它的事件是静态的。在一个引用被监听者的代表上发射事件是你倾向于注意的事情。

大多数实际对象模型都会尝试确保父进程时侦听器对象消失。 Windows Forms就是一个很好的例子。无需明确取消订阅事件。