问题 EventHandlers和匿名委托/ Lambda表达式


我希望用匿名委托和lambda表达式清除一些东西,用于在C#中为事件处理程序创建一个方法,至少对我自己来说。

假设我们有一个事件,它添加了一个匿名委托或一个lambda表达式(对于那些可以使用较新版本.NET的幸运人群)。

SomeClass.SomeEvent += delegate(object o, EventArg e) { /* do something */ };

我已经读过,过去的人们已经忘记了仍然有处理程序阻止该类被垃圾收集的事件。如何在不在类中将SomeEvent设置为null的情况下去除添加的处理程序。以下不是一个全新的处理程序吗?

SomeClass.SomeEvent -= delegate(object o, EventArg e) { /* do something */ };

我可以看到将匿名委托或lambda表达式存储在变量中。但至少对我而言,这似乎打败了能够简单而简洁地添加事件处理程序的全部目的。

SomeEventDelegate handler = new SomeEventDelegate(delegate(object o, EventArg e) { /* do something */ });
SomeClass.SomeEvent += handler;
// ... stuff
SomeClass.SomeEvent -= handler;

我再次理解你可以做到......

public override Dispose(bool disposing)
{
    _someEvent = null;
    this.Dispose();
}

但是我只是从事件中删除动态创建的方法更有趣。希望有人可以为我阐明这一点。谢谢!


12901
2018-01-13 14:44


起源

我有完全相同的问题,你设法表达得非常清楚。 - Stefano Ricciardi


答案:


如果对象X有一个事件处理程序 目标 对象Y,然后对象X是活动意味着对象Y不能被垃圾收集。它不会阻止对象X被垃圾收集。

通常当处理某些东西时,它很快就会变成垃圾,这意味着你没有问题。

事件和GC的问题是如果您忘记从a中删除订阅的处理程序 不同 对象 - 即你有一个被处理的监听器,但永远不会被垃圾收集,因为它仍然是来自不同对象中的事件的引用。


10
2018-01-13 14:58



委托事件处理程序的一个缺陷是,虽然它们似乎与定义它们的类断开连接,但您可能有一个编译器生成的闭包,它可以保持定义类实例的收集。 - Will
有趣的事实:当ToolStrip变得可见时,它会向System.UserPreferenceChanged注册。因此,如果从容器中删除ToolStrip而不将Visible设置为false,则永远不会处置它。这样的事情就是为什么迟早你需要一个内存分析器。 - Robert Rossney


我认为问题是你似乎正在假设将一个委托分配给一个对象的事件阻止它被GCed。

这作为一个简单的陈述是不正确的。

据说这个问题消失了。

最初在垃圾收集中,一切都是垃圾。 GC运行全局和每个堆栈上当前可用的所有内容以及它们引用的其他对象等等,将每个对象标记为非垃圾。

如此图形化过程如何设法达到这个目标?


1
2018-01-13 14:51



该陈述是正确的,因为事件处理程序被添加到内部管理的处理程序列表中。 - Kent Boogaart
如果你不小心的话,关闭可以咬你的屁股。 - Will
@Will:你真的需要自己解释一下。也许有你自己的答案和一个封闭如何咬人的例子? - AnthonyWJones
@Kent:你有关于这个内部管理的处理程序列表的参考吗? - AnthonyWJones
在这里寻找一口屁股: stackoverflow.com/questions/371109/... - Benjol


你不能。

就像你不能在其范围之外创建匿名类型(除了一些编译器技巧)。

这就是为什么它被称为匿名。

您必须在某处保存引用...或使用反射。


0
2018-01-13 15:01



我想它对于匿名对象来说是一样的。 SomeClass.SomeEvent + = new SomeEventHandler(SomeMethod);如果不将事件设置为null,将无法删除,不是吗? - Nicholas Mancuso