我正在使用基于事件的API,并希望只调用一次特定的事件处理方法。 (请注意,我有另一种方法总是处理事件,但我想为某些情况添加另一个处理程序并让它只执行一次。)
是否可以/鼓励取消订阅事件处理程序中的事件? E.g。
private void OnEventRaised(object sender, EventArgs e) {
_eventRaisingObject.EventRaised -= OnEventRaised;
... // Do normal code
}
在这种方法中我应该关注哪种交叉线程问题?
其次,当它是匿名方法时,是否可以只调用一次事件处理程序? E.g。
_eventRaisingObject.EventRaised += (sender, e) => {
// Unsubscribe?
... // Do normal code
}
目前,我使用的是匿名方法,只是检查一个布尔值,但我认为在完成工作时取消订阅会更好(开销和潜在的错误) - 也许这个假设是不正确的。我预见检查布尔值的一个问题是,如果情况需要重复(以便再次添加匿名事件处理程序),共享布尔值可能允许在不同线程上多次调用匿名方法。
答案 0 :(得分:2)
EventHandler handler;
handler = (sender, e) => {
_eventRaisingObject.EventRaised -= handler;
// Do normal code
}
_eventRaisingObject.EventRaised += handler;
应该可以工作,因为闭包捕获了handler和eventRaisingObject。
答案 1 :(得分:1)
由于需要处理事件的情况可能会多次出现,并且由于线程是一个问题,我建议在提升和处理条件之间建立更大的联系。
我不会动态添加和删除事件处理程序。它当然可以在事件处理程序中完成,但是在多线程环境中很难实现这一功能。
但出于同样的原因,我绝对不会使用布尔值。
您可以考虑创建一个队列。将一个项放入队列以指示事件处理程序需要响应事件并在响应时将一个项目从队列中取出(当然,在两侧都有适当的锁定),您可以更好地控制该过程
也许这对你的情况不起作用,但这是需要考虑的事情。
答案 2 :(得分:1)
可以取消订阅事件处理程序,但这里有明确的竞争条件。另一个线程也可能正在调用事件处理程序,在取消订阅之后获取名为的处理程序是非常可能的。您需要序列化对事件委托的访问权限:
public event EventHandler MyEvent;
private object eventLock = new object();
...
protected void OnMyEvent(EventArgs e) {
lock(eventLock) {
var handler = MyEvent;
if (handler != null) MyEvent(this, e);
}
}
这与保护您的布尔标志没有任何不同,尽管您可以使用Interlocked类提高效率。
答案 3 :(得分:0)
您可以将发件人转发给引发事件的对象并取消订阅。
void myRaisingObject_EventRaised(object sender, EventArgs e)
{
(sender as MyRaisingObject).EventRaised -= myRaisingObject_EventRaised;
}