取消订阅活动

时间:2010-01-21 10:28:36

标签: c# events anonymous-function

我有以下功能。

它的作用是,给定一个控件(很可能是一个窗体)我希望所有控件包含“遵守”规则(一个功能筛选我想要的控件)订阅一个事件(比如KeyDown)。

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

由于我将在表单上的表单的Load事件中使用此表单,如果表单关闭,我真的需要取消订阅吗?

(经过一些轻读和对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);
                }
            }

        }
    }

这是我第一次尝试使用事件,代表,匿名函数和lambdas,所以如果我做了一些非常愚蠢的事情告诉我。

3 个答案:

答案 0 :(得分:2)

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

是否需要取消订阅:想想对象的生命周期。您可以在静态方法中创建匿名函数,并附加我认为您可以控制生命周期的控件。

当您处置其中一个控件时,它们将不再引用匿名函数,并且GC可以终止它们(匿名函数),因此您不需要 取消订阅。

如果情况发生逆转并且静态方法中创建的内容引用了控件,就好像控件委托被添加到静态上下文中的事件一样,那么GC在引用之前无法处理控件删除了它们,如果在静态方法中完成则不会发生。

答案 1 :(得分:2)

如果您正在创建一次Form,并且这些处理程序在开始时也是一次,那么您根本不需要清理任何内容。

如果您多次创建它(例如,当用户点击按钮时多次创建表单),那么您需要小心。答案取决于处理程序的确切内容:

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

通常,将事件委托给事件可能会导致GC的依赖性循环,例如,想象一个Form包含控件A,并注册到A上的事件。然后在A处理之前不能处理该表单,并且在表单被处理之前不能处理A(因为它通过回调间接引用该表单)。如果您只与控件A一起创建表单,那么它的确定(GC将同时删除两者),但是当您动态创建控件A时,最终会出现内存泄漏。

答案 2 :(得分:0)

您可以使用

取消订阅活动
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的订阅方法,尽管b已被处理。