代码之家  ›  专栏  ›  技术社区  ›  Dan Rigby

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

  •  14
  • Dan Rigby  · 技术社区  · 15 年前

    假设我们有两个对象,广播者和听众。广播者有一个名为广播的事件,侦听器订阅该事件。如果在不取消订阅广播事件的情况下释放侦听器,则由于广播器包含的引用它的事件委托,侦听器将保留在内存中。

    我想知道的是,如果广播者在没有听众取消订阅或广播者设置broadcast=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 回复  |  直到 15 年前
        1
  •  9
  •   Marc Gravell    15 年前

    请注意,代理不会使发布服务器保持活动状态(它们只使target=subscriber保持活动状态),因此订阅的数量不会(单独)使广播服务器保持活动状态。因此,从这个角度来看,它是否被处置并不重要。如果没有引用广播公司的项目(并且事件订阅与此无关),则可以进行收集。

    基本上 ,委托是一对 MethodInfo object 引用;要调用的方法,以及要作为“arg0”调用的对象(aka this )它只是没有对象的引用 饲养 事件。

    有证据表明 保持源的活动状态;您应该看到收集了“源1”,即使我们还有订阅的匹配侦听器。如预期,“Listener 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
        }
    }
    
        2
  •  4
  •   Hans Passant    15 年前

    否。广播事件中的委托目标引用侦听器对象。这将使侦听器对象保持活动状态。侦听器对象没有返回广播对象的任何引用。

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

    在侦听器中不完全相同,它由它订阅的事件引用。如果被宣布不适合业务(已处理),但广播公司仍在运行,则应明确取消其事件订阅。SystemEvents类是它的一个极端版本,它的事件是静态的。在引用已释放侦听器的委托上激发事件是您可能注意到的事情。

    大多数实际的对象模型试图确保监听器对象在父对象离开时消失。Windows窗体就是一个很好的例子。那么就不需要显式地取消订阅事件。