4

我有以下功能。

它的作用是,给定一个控件(很可能是一个 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,所以如果我做了一些非常愚蠢的事情,请告诉我。

4

3 回答 3

2

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

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

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

一般来说,从 GC 的角度来看,将委托分配给事件可能会导致依赖循环,例如,假设一个 Form 包含控件 A,并在 A 上注册一个事件。那么在释放 A 之前无法释放表单,而 A 不能被释放直到表单被释放(因为它通过回调间接引用了表单)。如果您只与控件 A 一起创建表单,那么它可以(GC 将同时摆脱两者),但是当您动态创建控件 A 时,您最终可能会出现内存泄漏。

于 2010-01-21T11:03:56.650 回答
2

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

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

当您处置这些控件之一时,它们将不再引用匿名函数并且 GC 可以杀死它们(匿名函数),因此您无需取消订阅。

如果情况相反并且在静态方法中创建的东西引用了控件,就好像控件委托被添加到静态上下文中的事件一样,那么 GC 无法处理控件,直到对它们的引用被已删除,如果在静态方法中完成,则不会发生这种情况。

于 2010-01-21T10:50:20.020 回答
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 已被释放。

于 2010-01-21T10:51:23.520 回答