代码之家  ›  专栏  ›  技术社区  ›  Thanos Papathanasiou

取消订阅事件

  •  4
  • Thanos Papathanasiou  · 技术社区  · 15 年前

    我有以下功能。

    它的作用是,在给定控件(很可能是Windows窗体)的情况下,我希望拥有所有控件 包含 它“遵守”规则(一个屏蔽我想要的控件的函数)订阅一个事件(比如keydown)。

    问题是:如何取消订阅?更重要的是,我 需要 去?

    由于我将在窗体本身的窗体加载事件中使用此窗体,如果窗体关闭,我真的需要取消订阅吗?

    (在阅读了一些简单的内容并对GC有了一点了解之后,我怀疑我不需要退订,但我不确定)

    //an example of using the function
        private void Form1_Load(object sender, EventArgs e)
        {
            MyEventHandler.CreateKeyDownEventHandlers(this);
        }
    
    //the function
        public static void CreateEventHandlers(Control Ctrl)
        {
            foreach (Control c in Ctrl.Controls)
            {
                //bool Rules(Control) a function that determines to what controls'
                //events to apply the handler 
                if ( Rules(c) )
                {
                    c.KeyDown += (s, e) =>
                    {
                      // do something
                    };
    
                }
    
                //a control might be a groupbox so we want their contained
                //controls also
                if (c.Controls != null)
                {
                    if (c.Controls.Count > 0)
                    {
                        CreateEventHandlers(c);
                    }
                }
    
            }
        }
    

    这是我第一次尝试事件、委托、匿名函数和lambda,所以如果我做了一些非常愚蠢的事情,请告诉我。

    3 回复  |  直到 15 年前
        1
  •  2
  •   Johann Gerell    15 年前

    首先,我认为您不能取消订阅一个匿名函数,除非它被分配给一个处理程序变量,并且该变量被添加到事件中,然后从事件中删除。

    是否需要取消订阅:考虑对象的生命周期。您可以在静态方法中创建匿名函数,并将附加到我假定您控制其生存期的控件。

    当您释放其中一个控件时,它们将不再引用匿名函数,并且GC可以杀死它们(匿名函数),因此您不会 需要 退订。

    如果情况发生了逆转,并且在静态方法中创建的某个内容引用了控件,就像在静态上下文中向事件添加了控件委托一样,则在移除对它们的引用之前,GC无法处理这些控件,如果在静态方法中执行,则不会发生这种情况。

        2
  •  2
  •   Grzenio    15 年前

    如果您只创建了一次表单,并且这些处理程序在开始时也创建了一次,那么您实际上不需要清理任何东西。

    但是,如果多次创建表单(例如,当用户单击按钮时多次创建表单),则需要小心。这里的答案取决于处理程序中的具体内容:

    c.KeyDown += (s, e) =>
                {
                  // do something
                };
    

    通常,从GC的角度来说,将委托分配给一个事件可能会导致依赖循环,例如,假设一个表单包含控件A,并注册到a上的一个事件。那么,在释放a之前,无法释放该表单,在释放该表单之前,无法释放a(因为它通过回调间接引用该表单)。如果只与控件A一起创建窗体,那么它的OK(GC将同时删除这两个控件),但是当动态创建控件A时,可以以内存泄漏结束。

        3
  •  0
  •   Morfildur    15 年前

    您可以使用

    yourobject.Yourevent-= YourSubscribedFunction;
    

    这将从事件中取消订阅此函数。

    关于问题的第二部分:

    如果包含事件的对象被破坏,则不需要取消订阅。

    我不确定如果订阅对象被释放会发生什么,但是我的测试表明,即使对象不再存在,函数仍然被调用。

    ClassA a = new ClassA();
    using (ClassB b = new ClassB()) // implements IDisposable
    {
        b.SubscribeToFoo(a); // b subscribes to FooEvent of ClassA
        a.DoFoo(); // a executes FooEvent
    }
    GC.Collect(); // Run Garbage Collector just to be sure
    a.DoFoo(); // a executes FooEvent
    

    调用ClassB的订阅方法,eventhhough B被释放。