在 C# 中使用 Lambda 的一次性事件
我发现自己经常做这种事情:-
EventHandler eh = null; //can't assign lambda directly since it uses eh
eh = (s, args) =>
{
//small snippet of code here
((SomeType)s).SomeEvent -= eh;
}
variableOfSomeType.SomeEvent += eh;
基本上我只想附加一个事件处理程序来监听事件中的一个镜头,之后我不再想保持附加状态。通常,“代码片段”只是一行。
我的思绪变得有点麻木,我确信我必须做点什么,这样我就不需要重复所有这些开销。请记住,EventHandler
很可能是 EventHandler
。
有什么想法可以整理代码的重复部分并将代码片段留在 Lambda 中吗?
I find myself doing this sort of thing quite often:-
EventHandler eh = null; //can't assign lambda directly since it uses eh
eh = (s, args) =>
{
//small snippet of code here
((SomeType)s).SomeEvent -= eh;
}
variableOfSomeType.SomeEvent += eh;
Basically I only want to attach an event handler to listen for one shot from the event, I no longer want to stay attached after that. Quite often that "snippert of code" is just one line.
My mind is going a bit numb, I'm sure there must be something I can do so I don't need to repeat all this overhead. Bear in mind that EventHandler
may well be EventHandler<T>
.
Any ideas how I can tidy up the repeative part of the code and just leave the snippet in a Lambda?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
您可以将永久事件处理程序附加到该事件。然后,事件处理程序调用添加到内部队列的“一次性事件处理程序”:
代码:
测试类:
You could attache a permanent event handler to the event. The event handler then invokes "one shot event handlers" that are added to an internal queue:
Code:
Test class:
您可以使用反射:
像这样使用它:
You can use reflection:
Use it like so:
如果您可以使用 Reactive Extensions for .NET,则可以简化此操作。
您可以制作 可从事件中观察,并且仅使用
.Take(1)
监听第一个元素,以执行您的小代码片段。这将整个过程变成了几行代码。编辑:为了演示,我制作了一个完整的示例程序(我将粘贴在下面)。
我将可观察的创建和订阅移至方法中 (
HandleOneShot
)。这使您可以通过单个方法调用来完成您尝试的操作。为了进行演示,我创建了一个具有两个实现 INotifyPropertyChanged 的属性的类,并监听 first 属性更改事件,并在该事件发生时写入控制台。这将获取您的代码,并将其更改为:
请注意,所有订阅/取消订阅都会在幕后自动为您进行。无需手动处理订阅 - 只需订阅 Observable,Rx 就会为您处理这件事。
运行时,此代码将打印:
您只能获得事件的一次、一次性触发器。
If you can use the Reactive Extensions for .NET, you can simplify this.
You can make an Observable from an event, and only listen for the first element using
.Take(1)
, to do your small snippet of code. This turns this entire process into a couple of lines of code.Edit: In order to demonstrate, I've made a full sample program (I'll paste below).
I moved the observable creation and subscription into a method (
HandleOneShot
). This lets you do what you're attempting with a single method call. For demonstrating, I made a class with two properties that implements INotifyPropertyChanged, and am listening for the first property changed event, writing to the console when it occurs.This takes your code, and changes it to:
Notice that all of the subscription/unsubscription happens automatically for you behind the scenes. There's no need to handle putting in the subscription manually - just Subscribe to the Observable, and Rx takes care of this for you.
When run, this code prints:
You only get a single, one shot trigger of your event.
另一位用户遇到一个非常相似的问题,我相信该线程中的解决方案适用于此处。
特别是,您拥有的不是发布/订阅模式的实例,而是消息队列。使用
Queue{EventHandler}
创建您自己的消息队列非常容易,您可以在调用事件时将其出队。因此,您的“一次性”事件应该公开一种允许客户端将函数添加到消息队列的方法,而不是挂钩事件处理程序。
Another user encountered a very similar problem, and I believe the solution in that thread applies here.
In particular, what you have is not an instance of the publish/subscribe pattern, its a message queue. Its easy enough to create your own message queue using a
Queue{EventHandler}
, where you dequeue events as you invoke them.So instead of hooking on to an event handler, your "one-shot" events should expose a method allowing clients to add an function to the message queue.
有效吗?如果是这样,那么我说就去吧。对于一个看起来相当优雅的一次性事件。
我喜欢什么...
您也许能够概括它,但我不太确定如何概括,因为我似乎无法获得指向事件的指针。
Does it work? If so, then I say go for it. For a one-shot event that looks to be quite elegant.
What I like...
You might be able to generalize it, but I'm not entierly sure how to because I can't seem to get a pointer to a event.
就我个人而言,我只是为我正在处理的事件的任何类型创建一个专门的扩展方法。
这是我现在正在使用的一个基本版本:
我认为这是最好的解决方案的原因是因为您通常不需要只处理一次事件。您还经常需要检查事件是否已经通过...例如,这是上述扩展方法的另一个版本,它使用附加属性来检查元素是否已加载,在这种情况下,它只调用给定的处理程序马上:
Personally, I just create a specialized extension method for whatever type has the event I'm dealing with.
Here's a basic version of something I am using right now:
The reason I think this is the best solution is because you often don't need to just handle the event one time. You also often need to check if the event has already passed... For instance, here is another version of the above extension method that uses an attached property to check if the element is already loaded, in which case it just calls the given handler right away:
您可能想使用新的 async/await 习惯用法。
通常,当我需要像您描述的那样一次性执行事件处理程序时,我真正需要的是:
You probably want to work with the new async/await idioms.
Usually when I need to execute an event handler one-shot like you described, what I really need is something like:
为什么不使用事件内置的委托堆栈?
像...
Why not do use the delegate stack built into the event?
Something like...