我有以下功能。
它的作用是,给定一个控件(很可能是一个窗体)我希望所有控件包含“遵守”规则(一个功能筛选我想要的控件)订阅一个事件(比如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,所以如果我做了一些非常愚蠢的事情告诉我。
答案 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已被处理。