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

Dan*_*gby 14 c# events dispose

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

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

除了一个认为不将事件设置为null的博客将把源保留在内存中(在此处找到)之外,我无法找到任何对这个问题有一个难以回答的问题.

我想听听为什么或为什么不解释的原因.

谢谢.

更新: 论坛线程,其中一个开发人员指示事件应设置为null,但Jon Skeet表示没有必要,但没有详细说明.

Mar*_*ell 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
    }
}
Run Code Online (Sandbox Code Playgroud)


Han*_*ant 5

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

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

在侦听器中不太一样,它被它订阅的事件引用。如果它被宣布不适合业务(已处置)但广播公司仍在直播,那么它应该明确删除其事件订阅。SystemEvents 类是它的一个极端版本,它的事件是静态的。在引用已处置侦听器的委托上触发事件是您往往会注意到的事情。

大多数实用的对象模型都试图确保当父对象离开时侦听器对象消失。Windows 窗体就是一个很好的例子。那时无需显式取消订阅事件。