与 lambda 一起使用的弱事件处理程序模型
好的,所以这更像是一个答案而不是一个问题,但是在询问这个问题,并将 达斯汀·坎贝尔,Egor,还有来自“IObservable/Rx/Reactive 框架',我想我已经为这个特定问题找到了一个可行的解决方案。它可能会被 IObservable/Rx/Reactive 框架完全取代,但只有经验才能证明这一点。
我故意创建了一个新问题,以便给我空间来解释我如何得到这个解决方案,因为它可能不会立即显而易见。
有许多相关的问题,大多数告诉您如果您希望稍后能够分离它们,则不能使用内联 lambda:
- .Net 中的弱事件?
- 在 C# 中使用 lambda 取消挂钩事件< /a>
- 使用 lambda 作为事件处理程序会导致内存泄漏泄漏?
- 如何取消订阅使用 lambda 表达式的事件?
- 取消订阅 C# 中的匿名方法
以及确实,如果您希望稍后能够分离它们,则需要保留对 lambda 的引用。但是,如果您只是希望事件处理程序在您的订阅者超出范围时自行分离,那么这个答案适合您。
OK, so this is more of an answer than a question, but after asking this question, and pulling together the various bits from Dustin Campbell, Egor, and also one last tip from the 'IObservable/Rx/Reactive framework', I think I've worked out a workable solution for this particular problem. It may be completely superseded by IObservable/Rx/Reactive framework, but only experience will show that.
I've deliberately created a new question, to give me space to explain how I got to this solution, as it may not be immediately obvious.
There are many related questions, most telling you you can't use inline lambdas if you want to be able to detach them later:
- Weak events in .Net?
- Unhooking events with lambdas in C#
- Can using lambdas as event handlers cause a memory leak?
- How to unsubscribe from an event which uses a lambda expression?
- Unsubscribe anonymous method in C#
And it is true that if YOU want to be able to detach them later, you need to keep a reference to your lambda. However, if you just want the event handler to detach itself when your subscriber falls out of scope, this answer is for you.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
'The' 答案
(如果您想了解我如何得到这个解决方案,请阅读下面的更多内容)
用法,给定一个带有普通
MouseDown
事件的控件和一个特定的EventHandler; ValueEvent
事件:(*这是来自 Rx)
(** 避免直接在此处调用订阅者对象非常重要(例如放置订阅者.DoSomething(e),或调用 DoSomething( e) 如果我们在 Subscriber 类内部,则直接这样做会有效地创建对订阅者的引用,这完全破坏了该对象...)
注意:在某些情况下,这可以留下对包装类的引用。为内存中的 lambda 创建,但它们只重字节,所以我不太担心
实现:
细节
我的出发点是 Egor 的出色回答(请参阅带评论的版本链接):
令我困扰的是该事件被硬编码到方法中这意味着对于每个新事件,都有一个新的方法可以编写。
我摆弄并设法想出了这个通用的解决方案:
但是该解决方案的问题是它只是通用的,它无法处理标准的 winforms MouseUp、MouseDown 等...
所以我试图使它甚至 <强>更通用:
但是,正如我暗示的这里,这不会编译,因为没有办法限制 T 成为委托。
那一刻,我几乎放弃了。尝试与 C# 规范作斗争是没有意义的。
然而,昨天,我发现了Reactive框架中的Observable.FromEvent方法,我没有实现,但用法似乎有点熟悉,而且很有趣:
这是引起我注意的第一个参数。这是缺少委托类型约束的解决方法。我们通过传入创建委托的函数来获取它。
将所有这些放在一起,我们就得到了本答案顶部所示的解决方案。
事后
我彻底建议花时间了解反应式框架(或者无论它最终被称为什么)。这非常有趣,而且有点令人兴奋。我怀疑它也会使这样的问题变得完全多余。
到目前为止,我见过的最有趣的内容是 Channel9 上的视频。
'The' answer
(Read more below if you want to see how I got to this solution)
Usage, given a control with a vanilla
MouseDown
event, and a specificEventHandler<ValueEventArgs> ValueEvent
event:(*This is a workaround from Rx)
(** it is important to avoid invoking the subscriber object directly here (for instance putting subscriber.DoSomething(e), or invoking DoSomething(e) directly if we are inside the Subscriber class. Doing this effectively creates a reference to subscriber, which completely defeats the object...)
Note: in some circumstances, this CAN leave references to the wrapping classes created for the lambdas in memory, but they only weigh bytes, so I'm not too bothered.
Implementation:
The detail
My starting point was Egor's excellent answer (see link for version with comments):
What bothered me was that the event is hard coded into the method. So that means for each new event, there is a new method to write.
I fiddled around and managed to come up with this generic solution:
However the problem with that solution is that it is ONLY generic, it can't handle the standard winforms MouseUp, MouseDown, etc...
So I tried to make it even more generic:
However, as I hinted here, this won't compile, because there is no way of constraining T to be a delegate.
At that point, I pretty much gave up. There's no point trying to fight with the C# specs.
However, yesterday, I discovered the Observable.FromEvent method from the Reactive framework, I didn't have the implementation, but the usage seemed slightly familiar, and very interesting:
It was the first argument that caught my attention. This is the workaround for the absence of a delegate type constraint. We take of it by passing in the function which will create the delegate.
Putting all this together gives us the solution shown at the top of this answer.
Afterthought
I thoroughly recommended taking the time to learn about the reactive framework (or whatever it ends up being called). It is VERY interesting, and slightly mindblowing. I suspect that it will also render questions like this totally redundant.
So far, the most interesting stuff I've seen has been the videos on Channel9.
如果您访问 CodePlex,有一个名为 Sharp Observation 的项目,其中作者构建了一个良好的弱委托提供程序,并在 MSIL 中实现。快速、灵活、易于使用:
例如
,就这么简单!
If you head over to CodePlex there's a project called Sharp Observation in which the author has built a good weak delegate provider, implemented in MSIL. Fast, flexible, easy to use:
e.g.
As easy as that!
我长期以来一直在寻找解决方案,大多数人都使用令人讨厌的反射,但 Benjohl 的答案很棒。我对其进行了调整,添加了对非通用 EventHandler、DependencyPropertyChangedEventArgs 的支持,它不继承自 EventArgs,并允许您自己手动取消注册事件。我对人们的想法非常感兴趣,尤其是 Benjohl。
I have been looking for a solution for a long time and most use nasty reflection but Benjohl's answer is great. I have tweaked it to add support for non-generic EventHandler, DependencyPropertyChangedEventArgs which does not inherit from EventArgs and to allow you to manually unregister an event yourself. I would be very interested in peoples thoughts, especially Benjohl.
达斯汀·坎贝尔的方法已经很出色了。剩下的唯一的事情,保存集成到 .NET 中的解决方案,是创建真正通用的弱事件处理程序的非常简单的方法:
http://puremsil.wordpress.com/2010/05/03/generic-weak-event-handlers/
Dustin Campbell's approach is already excellent. The only thing left, save a solution integrated into .NET, is a really simple way to create really generic weak event handlers:
http://puremsil.wordpress.com/2010/05/03/generic-weak-event-handlers/